auth0-flutter-native

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Auth0 Flutter Native (iOS/Android) Integration

Auth0 Flutter 原生(iOS/Android)集成

auth0_flutter
is the official Auth0 SDK for Flutter applications. On mobile (iOS/Android), it performs authentication through the device's system browser (Web Auth / Universal Login) and stores tokens securely in the platform keychain/keystore via the built-in
CredentialsManager
, with optional biometric protection.
Agent instruction: Before providing SDK setup instructions, fetch the latest release version by running one of:
bash
gh api repos/auth0/auth0-flutter/releases/latest --jq '.tag_name'
bash
flutter pub info auth0_flutter 2>/dev/null | head -5
Or check pub.dev:
bash
curl -s https://pub.dev/api/packages/auth0_flutter | python3 -c "import sys,json;print(json.load(sys.stdin)['latest']['version'])"
Use the returned version in all dependency lines instead of any hardcoded version below. Current known version:
2.1.0
.
auth0_flutter
是Auth0官方推出的Flutter应用开发SDK。在移动端(iOS/Android),它通过设备的系统浏览器(Web Auth / Universal Login)执行身份验证,并通过内置的
CredentialsManager
将令牌安全存储在平台的密钥链/密钥库中,还支持可选的生物识别保护。
Agent指令: 在提供SDK设置说明之前,通过运行以下命令之一获取最新版本:
bash
gh api repos/auth0/auth0-flutter/releases/latest --jq '.tag_name'
bash
flutter pub info auth0_flutter 2>/dev/null | head -5
或者查看pub.dev:
bash
curl -s https://pub.dev/api/packages/auth0_flutter | python3 -c "import sys,json;print(json.load(sys.stdin)['latest']['version'])"
在所有依赖项行中使用返回的版本,而不是下面任何硬编码的版本。当前已知版本:
2.1.0

When NOT to Use

不适用场景

Prerequisites

前置条件

  • Flutter 3.24.0+
  • Dart 3.5.0+
  • Android: minSdkVersion 21+, compileSdkVersion 34+
  • iOS: 14.0+ (Universal Link callbacks require iOS 17.4+)
  • Auth0 account — Sign up free
  • Auth0 CLI — install instructions (used to create and configure the Auth0 application)
  • Flutter 3.24.0+
  • Dart 3.5.0+
  • Android:minSdkVersion 21+,compileSdkVersion 34+
  • iOS:14.0+(Universal Link回调需要iOS 17.4+)
  • Auth0账户——免费注册
  • Auth0 CLI——安装说明(用于创建和配置Auth0应用)

Quick Start Workflow

快速开始流程

Agent instruction: Follow these steps in order. If you encounter an error at any step, attempt to fix it up to 5 times before calling
AskUserQuestion
to ask the user for guidance. Always search existing code first — if there are existing login/logout handlers, hook into them rather than creating new ones.
Agent指令: 按顺序执行以下步骤。如果在任何步骤中遇到错误,尝试修复最多5次后,再调用
AskUserQuestion
向用户寻求指导。始终先搜索现有代码——如果已有登录/登出处理程序,应接入现有程序而非创建新程序。

Step 1 — Install SDK

步骤1——安装SDK

Agent instruction: Check the project directory for
pubspec.yaml
. If present, add the dependency. If not found, this is not a Flutter project — ask the user.
Run in the project root:
bash
flutter pub add auth0_flutter
Verify the dependency was added to
pubspec.yaml
:
yaml
dependencies:
  auth0_flutter: ^2.1.0
Agent指令: 检查项目目录中是否存在
pubspec.yaml
。如果存在,添加依赖项。如果未找到,则这不是Flutter项目——请询问用户。
在项目根目录运行:
bash
flutter pub add auth0_flutter
验证依赖项已添加到
pubspec.yaml
yaml
dependencies:
  auth0_flutter: ^2.1.0

Step 2 — Configure Auth0

步骤2——配置Auth0

Note: The Auth0 Domain and Client ID are public configuration (not secrets) — a native app uses PKCE with no client secret. Pass them directly to
Auth0(domain, clientId)
; there is no need to store them in environment variables or hide them.
Agent instruction:
  • If Auth0 credentials (domain AND client ID) are already in the user's prompt: Use those values directly in the
    Auth0(...)
    constructor and proceed to Step 3.
  • If no credentials are provided: Ask the user which setup they prefer using
    AskUserQuestion
    : "How would you like to set up the Auth0 application — automatic (I run the Auth0 CLI to create it) or manual (you create it in the Auth0 Dashboard and give me the Domain + Client ID)?"
    • Automatic: Follow the Auth0 CLI steps in the Setup Guide to create the Native application.
    • Manual: Ask the user for their Auth0 Domain and Client ID and use them directly.
Follow Setup Guide — Auth0 Configuration for the pre-flight checks and the
auth0 apps create
command.
注意: Auth0域名和客户端ID是公开配置项(不是机密)——原生应用使用PKCE,无需客户端密钥。直接将它们传递给
Auth0(domain, clientId)
;无需将它们存储在环境变量中或隐藏。
Agent指令:
  • 如果用户的提示中已提供Auth0凭证(域名和客户端ID): 直接在
    Auth0(...)
    构造函数中使用这些值,然后进入步骤3。
  • 如果未提供凭证: 使用
    AskUserQuestion
    询问用户偏好的设置方式:“您希望如何设置Auth0应用——自动方式(我运行Auth0 CLI创建)还是手动方式(您在Auth0控制台创建并提供域名+客户端ID)?”
    • 自动方式: 按照设置指南中的Auth0 CLI步骤创建原生应用。
    • 手动方式: 询问用户的Auth0域名和客户端ID并直接使用。
遵循设置指南——Auth0配置进行预检检查和
auth0 apps create
命令操作。

Step 3 — Configure Android

步骤3——配置Android

Agent instruction: Edit
android/app/build.gradle
(or
build.gradle.kts
) and add
manifestPlaceholders
inside
android { defaultConfig { ... } }
. These supply the callback URL the SDK's
RedirectActivity
intent filter registers — without them the app will not build correctly for Auth0.
For
android/app/build.gradle
(Groovy):
groovy
android {
    defaultConfig {
        manifestPlaceholders = [auth0Domain: "YOUR_AUTH0_DOMAIN", auth0Scheme: "https"]
    }
}
For
android/app/build.gradle.kts
(Kotlin DSL):
kotlin
android {
    defaultConfig {
        manifestPlaceholders["auth0Domain"] = "YOUR_AUTH0_DOMAIN"
        manifestPlaceholders["auth0Scheme"] = "https"
    }
}
Agent instruction: Use
auth0Scheme: "https"
to use Android App Links (recommended). If the app targets a custom scheme instead, set it to a lowercase scheme string and pass the same scheme to
webAuthentication(scheme: ...)
in Dart. See Setup Guide for details.
Agent指令: 编辑
android/app/build.gradle
(或
build.gradle.kts
),在
android { defaultConfig { ... } }
内添加
manifestPlaceholders
。这些值提供SDK的
RedirectActivity
意图过滤器注册的回调URL——没有这些值,应用将无法针对Auth0正确构建。
对于
android/app/build.gradle
(Groovy):
groovy
android {
    defaultConfig {
        manifestPlaceholders = [auth0Domain: "YOUR_AUTH0_DOMAIN", auth0Scheme: "https"]
    }
}
对于
android/app/build.gradle.kts
(Kotlin DSL):
kotlin
android {
    defaultConfig {
        manifestPlaceholders["auth0Domain"] = "YOUR_AUTH0_DOMAIN"
        manifestPlaceholders["auth0Scheme"] = "https"
    }
}
Agent指令: 使用
auth0Scheme: "https"
来使用Android App Links(推荐)。如果应用使用自定义scheme,请将其设置为小写的scheme字符串,并在Dart代码中传递相同的scheme给
webAuthentication(scheme: ...)
。详情请参见设置指南

Step 4 — Configure iOS

步骤4——配置iOS

Agent instruction: For the default HTTPS (Universal Link) flow on iOS 17.4+, no
Info.plist
change is required, but the Associated Domains capability must be added in Xcode (
webcredentials:YOUR_AUTH0_DOMAIN
). For older iOS or a custom URL scheme, add a
CFBundleURLTypes
entry to
ios/Runner/Info.plist
:
xml
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>None</string>
    <key>CFBundleURLName</key>
    <string>auth0</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    </array>
  </dict>
</array>
Agent指令: 对于iOS 17.4+上默认的HTTPS(Universal Link)流程,无需修改
Info.plist
,但必须在Xcode中添加关联域功能(
webcredentials:YOUR_AUTH0_DOMAIN
)。对于旧版iOS或自定义URL scheme,需向
ios/Runner/Info.plist
添加
CFBundleURLTypes
条目:
xml
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>None</string>
    <key>CFBundleURLName</key>
    <string>auth0</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    </array>
  </dict>
</array>

Step 5 — Configure Callback URLs

步骤5——配置回调URL

Agent instruction: Register the platform-specific callback and logout URLs using the Auth0 CLI. Determine the Android package name (from
android/app/build.gradle
applicationId
) and the iOS bundle identifier (from Xcode /
PRODUCT_BUNDLE_IDENTIFIER
), then run the command below, replacing the placeholders (
CLIENT_ID
,
YOUR_DOMAIN
,
ANDROID_PACKAGE_NAME
,
IOS_BUNDLE_ID
) with the project's values:
bash
auth0 apps update CLIENT_ID \
  --callbacks "https://YOUR_DOMAIN/android/ANDROID_PACKAGE_NAME/callback,https://YOUR_DOMAIN/ios/IOS_BUNDLE_ID/callback" \
  --logout-urls "https://YOUR_DOMAIN/android/ANDROID_PACKAGE_NAME/callback,https://YOUR_DOMAIN/ios/IOS_BUNDLE_ID/callback" \
  --no-input
The callback URL formats are:
  • Android:
    https://YOUR_DOMAIN/android/YOUR_PACKAGE_NAME/callback
  • iOS:
    https://YOUR_DOMAIN/ios/YOUR_BUNDLE_ID/callback
Agent指令: 使用Auth0 CLI注册平台特定的回调和登出URL。确定Android包名(来自
android/app/build.gradle
applicationId
)和iOS bundle标识符(来自Xcode /
PRODUCT_BUNDLE_IDENTIFIER
),然后运行以下命令,将占位符(
CLIENT_ID
,
YOUR_DOMAIN
,
ANDROID_PACKAGE_NAME
,
IOS_BUNDLE_ID
)替换为项目的值:
bash
auth0 apps update CLIENT_ID \
  --callbacks "https://YOUR_DOMAIN/android/ANDROID_PACKAGE_NAME/callback,https://YOUR_DOMAIN/ios/IOS_BUNDLE_ID/callback" \
  --logout-urls "https://YOUR_DOMAIN/android/ANDROID_PACKAGE_NAME/callback,https://YOUR_DOMAIN/ios/IOS_BUNDLE_ID/callback" \
  --no-input
回调URL格式:
  • Android
    https://YOUR_DOMAIN/android/YOUR_PACKAGE_NAME/callback
  • iOS
    https://YOUR_DOMAIN/ios/YOUR_BUNDLE_ID/callback

Step 6 — Implement Authentication

步骤6——实现身份验证

Agent instruction: Search the project for the main app entry point (
main.dart
). Determine the state management approach:
  • Look for
    provider
    ,
    riverpod
    ,
    bloc
    ,
    GetX
    , or
    mobx
    imports
  • If none found, use basic
    StatefulWidget
    with
    setState
Then follow only the matching path below. If ambiguous, ask via
AskUserQuestion
: "Which state management approach does your Flutter app use — Provider, Riverpod, Bloc, or basic setState?"
Agent指令: 搜索项目的主应用入口点(
main.dart
)。确定状态管理方式:
  • 查找
    provider
    riverpod
    bloc
    GetX
    mobx
    导入
  • 如果未找到,使用基础的
    StatefulWidget
    配合
    setState
然后仅遵循以下匹配的流程。如果不确定,通过
AskUserQuestion
询问:“您的Flutter应用使用哪种状态管理方式——Provider、Riverpod、Bloc还是基础的setState?”

Basic StatefulWidget (Default)

基础StatefulWidget(默认)

Agent instruction: Create an
AuthService
class, then wire it into the app's root widget. Search for the
MaterialApp
or
CupertinoApp
widget and update accordingly. On startup, restore the session from the
CredentialsManager
cache.
dart
// lib/auth_service.dart
import 'package:auth0_flutter/auth0_flutter.dart';

class AuthService {
  late final Auth0 _auth0;
  Credentials? _credentials;

  AuthService({required String domain, required String clientId}) {
    _auth0 = Auth0(domain, clientId);
  }

  bool get isAuthenticated => _credentials != null;
  UserProfile? get user => _credentials?.user;

  /// Restore a stored session on app startup, if one exists.
  Future<void> init() async {
    final hasValid = await _auth0.credentialsManager.hasValidCredentials();
    if (hasValid) {
      _credentials = await _auth0.credentialsManager.credentials();
    }
  }

  /// Launch Web Auth via the system browser. Tokens are stored automatically.
  Future<void> login() async {
    _credentials = await _auth0
        .webAuthentication()
        .login(scopes: {'openid', 'profile', 'email', 'offline_access'});
  }

  /// Clear the session in the browser and wipe stored credentials.
  Future<void> logout() async {
    await _auth0.webAuthentication().logout();
    await _auth0.credentialsManager.clearCredentials();
    _credentials = null;
  }
}
dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:auth0_flutter/auth0_flutter.dart'; // for WebAuthenticationException
import 'auth_service.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _authService = AuthService(
    domain: 'YOUR_AUTH0_DOMAIN',
    clientId: 'YOUR_AUTH0_CLIENT_ID',
  );
  bool _isLoading = true;

  
  void initState() {
    super.initState();
    _initAuth();
  }

  Future<void> _initAuth() async {
    await _authService.init();
    setState(() => _isLoading = false);
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: _isLoading
          ? const Scaffold(body: Center(child: CircularProgressIndicator()))
          : _authService.isAuthenticated
              ? HomeScreen(authService: _authService, onChanged: _refresh)
              : LoginScreen(authService: _authService, onChanged: _refresh),
    );
  }

  void _refresh() => setState(() {});
}

class LoginScreen extends StatelessWidget {
  final AuthService authService;
  final VoidCallback onChanged;
  const LoginScreen({super.key, required this.authService, required this.onChanged});

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            // Capture the messenger before the await to avoid using
            // BuildContext across an async gap.
            final messenger = ScaffoldMessenger.of(context);
            try {
              await authService.login();
              onChanged();
            } on WebAuthenticationException catch (e) {
              messenger.showSnackBar(
                SnackBar(content: Text('Login failed: ${e.message}')),
              );
            }
          },
          child: const Text('Log In'),
        ),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final AuthService authService;
  final VoidCallback onChanged;
  const HomeScreen({super.key, required this.authService, required this.onChanged});

  
  Widget build(BuildContext context) {
    final user = authService.user;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        actions: [
          IconButton(
            onPressed: () async {
              await authService.logout();
              onChanged();
            },
            icon: const Icon(Icons.logout),
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (user?.pictureUrl != null)
              CircleAvatar(
                backgroundImage: NetworkImage(user!.pictureUrl.toString()),
                radius: 40,
              ),
            const SizedBox(height: 16),
            Text('Welcome, ${user?.name ?? 'User'}!'),
            Text(user?.email ?? ''),
          ],
        ),
      ),
    );
  }
}
Agent指令: 创建
AuthService
类,然后将其接入应用的根组件。查找
MaterialApp
CupertinoApp
组件并相应更新。启动时,从
CredentialsManager
缓存中恢复会话。
dart
// lib/auth_service.dart
import 'package:auth0_flutter/auth0_flutter.dart';

class AuthService {
  late final Auth0 _auth0;
  Credentials? _credentials;

  AuthService({required String domain, required String clientId}) {
    _auth0 = Auth0(domain, clientId);
  }

  bool get isAuthenticated => _credentials != null;
  UserProfile? get user => _credentials?.user;

  /// Restore a stored session on app startup, if one exists.
  Future<void> init() async {
    final hasValid = await _auth0.credentialsManager.hasValidCredentials();
    if (hasValid) {
      _credentials = await _auth0.credentialsManager.credentials();
    }
  }

  /// Launch Web Auth via the system browser. Tokens are stored automatically.
  Future<void> login() async {
    _credentials = await _auth0
        .webAuthentication()
        .login(scopes: {'openid', 'profile', 'email', 'offline_access'});
  }

  /// Clear the session in the browser and wipe stored credentials.
  Future<void> logout() async {
    await _auth0.webAuthentication().logout();
    await _auth0.credentialsManager.clearCredentials();
    _credentials = null;
  }
}
dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:auth0_flutter/auth0_flutter.dart'; // for WebAuthenticationException
import 'auth_service.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _authService = AuthService(
    domain: 'YOUR_AUTH0_DOMAIN',
    clientId: 'YOUR_AUTH0_CLIENT_ID',
  );
  bool _isLoading = true;

  
  void initState() {
    super.initState();
    _initAuth();
  }

  Future<void> _initAuth() async {
    await _authService.init();
    setState(() => _isLoading = false);
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: _isLoading
          ? const Scaffold(body: Center(child: CircularProgressIndicator()))
          : _authService.isAuthenticated
              ? HomeScreen(authService: _authService, onChanged: _refresh)
              : LoginScreen(authService: _authService, onChanged: _refresh),
    );
  }

  void _refresh() => setState(() {});
}

class LoginScreen extends StatelessWidget {
  final AuthService authService;
  final VoidCallback onChanged;
  const LoginScreen({super.key, required this.authService, required this.onChanged});

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            // Capture the messenger before the await to avoid using
            // BuildContext across an async gap.
            final messenger = ScaffoldMessenger.of(context);
            try {
              await authService.login();
              onChanged();
            } on WebAuthenticationException catch (e) {
              messenger.showSnackBar(
                SnackBar(content: Text('Login failed: ${e.message}')),
              );
            }
          },
          child: const Text('Log In'),
        ),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final AuthService authService;
  final VoidCallback onChanged;
  const HomeScreen({super.key, required this.authService, required this.onChanged});

  
  Widget build(BuildContext context) {
    final user = authService.user;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        actions: [
          IconButton(
            onPressed: () async {
              await authService.logout();
              onChanged();
            },
            icon: const Icon(Icons.logout),
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (user?.pictureUrl != null)
              CircleAvatar(
                backgroundImage: NetworkImage(user!.pictureUrl.toString()),
                radius: 40,
              ),
            const SizedBox(height: 16),
            Text('Welcome, ${user?.name ?? 'User'}!'),
            Text(user?.email ?? ''),
          ],
        ),
      ),
    );
  }
}

Provider (State Management)

Provider(状态管理)

Agent instruction: If the project uses
provider
, create
AuthService
as a
ChangeNotifier
and inject it via
ChangeNotifierProvider
at the app root.
dart
// lib/auth_service.dart
import 'package:flutter/foundation.dart';
import 'package:auth0_flutter/auth0_flutter.dart';

class AuthService extends ChangeNotifier {
  late final Auth0 _auth0;
  Credentials? _credentials;
  bool _isLoading = true;

  AuthService({required String domain, required String clientId}) {
    _auth0 = Auth0(domain, clientId);
  }

  bool get isAuthenticated => _credentials != null;
  bool get isLoading => _isLoading;
  UserProfile? get user => _credentials?.user;

  Future<void> init() async {
    if (await _auth0.credentialsManager.hasValidCredentials()) {
      _credentials = await _auth0.credentialsManager.credentials();
    }
    _isLoading = false;
    notifyListeners();
  }

  Future<void> login() async {
    _credentials = await _auth0
        .webAuthentication()
        .login(scopes: {'openid', 'profile', 'email', 'offline_access'});
    notifyListeners();
  }

  Future<void> logout() async {
    await _auth0.webAuthentication().logout();
    await _auth0.credentialsManager.clearCredentials();
    _credentials = null;
    notifyListeners();
  }
}
dart
// lib/main.dart — wrap with ChangeNotifierProvider
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => AuthService(
        domain: 'YOUR_AUTH0_DOMAIN',
        clientId: 'YOUR_AUTH0_CLIENT_ID',
      )..init(),
      child: const MyApp(),
    ),
  );
}
For complete patterns with Riverpod, Bloc, biometrics, and advanced scenarios, see Integration Patterns.
Agent指令: 如果项目使用
provider
,将
AuthService
创建为
ChangeNotifier
,并通过
ChangeNotifierProvider
在应用根节点注入。
dart
// lib/auth_service.dart
import 'package:flutter/foundation.dart';
import 'package:auth0_flutter/auth0_flutter.dart';

class AuthService extends ChangeNotifier {
  late final Auth0 _auth0;
  Credentials? _credentials;
  bool _isLoading = true;

  AuthService({required String domain, required String clientId}) {
    _auth0 = Auth0(domain, clientId);
  }

  bool get isAuthenticated => _credentials != null;
  bool get isLoading => _isLoading;
  UserProfile? get user => _credentials?.user;

  Future<void> init() async {
    if (await _auth0.credentialsManager.hasValidCredentials()) {
      _credentials = await _auth0.credentialsManager.credentials();
    }
    _isLoading = false;
    notifyListeners();
  }

  Future<void> login() async {
    _credentials = await _auth0
        .webAuthentication()
        .login(scopes: {'openid', 'profile', 'email', 'offline_access'});
    notifyListeners();
  }

  Future<void> logout() async {
    await _auth0.webAuthentication().logout();
    await _auth0.credentialsManager.clearCredentials();
    _credentials = null;
    notifyListeners();
  }
}
dart
// lib/main.dart — wrap with ChangeNotifierProvider
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => AuthService(
        domain: 'YOUR_AUTH0_DOMAIN',
        clientId: 'YOUR_AUTH0_CLIENT_ID',
      )..init(),
      child: const MyApp(),
    ),
  );
}
有关Riverpod、Bloc、生物识别和高级场景的完整模式,请参见集成模式

Step 7 — Verify Build

步骤7——验证构建

Agent instruction: Run a build to verify the integration compiles without errors:
bash
flutter build apk --debug      # Android
flutter build ios --no-codesign  # iOS (on macOS)
Then run the app on a device or emulator to test:
bash
flutter run
If the build fails, review error messages and fix up to 5 times before asking the user.
Physical device testing: Biometric protection (Face ID / Touch ID / fingerprint) cannot be exercised on a simulator/emulator — the iOS Simulator and Android emulator have limited or no biometric hardware. Test biometrics and the full Universal Login redirect on a real physical device before release.
Agent指令: 运行构建以验证集成编译无错误:
bash
flutter build apk --debug      # Android
flutter build ios --no-codesign  # iOS (on macOS)
然后在设备或模拟器上运行应用进行测试:
bash
flutter run
如果构建失败,查看错误信息并尝试修复最多5次后再询问用户。
物理设备测试: 生物识别保护(面容ID / 触控ID / 指纹)无法在模拟器/仿真器上测试——iOS模拟器和Android仿真器的生物识别硬件有限或没有。发布前请在真实物理设备上测试生物识别和完整的Universal Login重定向流程。

Detailed Documentation

详细文档

  • Setup Guide — Auth0 application creation via the Auth0 CLI, Android
    manifestPlaceholders
    , iOS
    Info.plist
    / Associated Domains, callback URL registration
  • Integration Patterns — Web Auth login/logout, CredentialsManager, biometric protection, custom schemes, organizations, API access tokens, state management patterns, error handling
  • API Reference & Testing — Full API reference, configuration options, claims reference, testing checklist, troubleshooting
  • 设置指南——通过Auth0 CLI创建Auth0应用、Android
    manifestPlaceholders
    、iOS
    Info.plist
    / 关联域、回调URL注册
  • 集成模式——Web Auth登录/登出、CredentialsManager、生物识别保护、自定义scheme、组织、API访问令牌、状态管理模式、错误处理
  • API参考与测试——完整API参考、配置选项、声明参考、测试清单、故障排除

Common Mistakes

常见错误

MistakeFix
Auth0 app type not set to NativeCreate the application with
auth0 apps create --type native
(or select "Native" in the Auth0 Dashboard)
Missing
manifestPlaceholders
on Android
Add
manifestPlaceholders = [auth0Domain: "...", auth0Scheme: "https"]
to
android/app/build.gradle
defaultConfig
— the build fails without it
Using
Auth0Web
on mobile
Mobile uses the
Auth0
class with
webAuthentication()
, not
Auth0Web
(that's the web-only API)
Importing
auth0_flutter_web.dart
on mobile
Only import
package:auth0_flutter/auth0_flutter.dart
— the
_web
import is for Flutter web
Callback URL mismatchRegister
https://YOUR_DOMAIN/android/PACKAGE_NAME/callback
and
https://YOUR_DOMAIN/ios/BUNDLE_ID/callback
in Allowed Callback URLs
Scheme mismatch between Gradle and DartIf
auth0Scheme
is a custom scheme, pass the same value to
webAuthentication(scheme: 'myscheme')
Custom scheme with uppercase letters on AndroidAndroid custom schemes must be all lowercase
Biometrics prompt never appears on Android
MainActivity
must extend
FlutterFragmentActivity
(not
FlutterActivity
) for the biometric prompt to work
Not storing credentials after login
webAuthentication().login()
stores credentials automatically; do NOT also re-store unless renewing manually via
api.renewCredentials
Not restoring session on startupCall
credentialsManager.hasValidCredentials()
+
credentials()
in
initState()
to restore the session
Missing
offline_access
scope
Add
'offline_access'
to scopes so the CredentialsManager can silently renew expired access tokens with a refresh token
Catching generic
Exception
Catch
WebAuthenticationException
(login/logout) and
CredentialsManagerException
(credential errors) and inspect
isUserCancelledException
,
isNoCredentialsFound
,
isTokenRenewFailed
, etc.
错误修复方法
Auth0应用类型未设置为Native使用
auth0 apps create --type native
创建应用(或在Auth0控制台中选择“Native”)
Android缺失
manifestPlaceholders
android/app/build.gradle
defaultConfig
中添加
manifestPlaceholders = [auth0Domain: "...", auth0Scheme: "https"]
——没有它构建会失败
在移动端使用
Auth0Web
移动端使用
Auth0
类配合
webAuthentication()
,而非
Auth0Web
(这是Web端专用API)
在移动端导入
auth0_flutter_web.dart
仅导入
package:auth0_flutter/auth0_flutter.dart
——
_web
导入仅适用于Flutter Web
回调URL不匹配在允许的回调URL中注册
https://YOUR_DOMAIN/android/PACKAGE_NAME/callback
https://YOUR_DOMAIN/ios/BUNDLE_ID/callback
Gradle与Dart中的scheme不匹配如果
auth0Scheme
是自定义scheme,将相同的值传递给
webAuthentication(scheme: 'myscheme')
Android自定义scheme包含大写字母Android自定义scheme必须全小写
Android上生物识别提示从未出现
MainActivity
必须继承
FlutterFragmentActivity
(而非
FlutterActivity
)才能使生物识别提示正常工作
登录后未存储凭证
webAuthentication().login()
会自动存储凭证;除非通过
api.renewCredentials
手动刷新,否则请勿重复存储
启动时未恢复会话
initState()
中调用
credentialsManager.hasValidCredentials()
+
credentials()
来恢复会话
缺失
offline_access
scope
添加
'offline_access'
到scopes中,以便CredentialsManager可以使用刷新令牌静默刷新过期的访问令牌
捕获通用
Exception
捕获
WebAuthenticationException
(登录/登出)和
CredentialsManagerException
(凭证错误),并检查
isUserCancelledException
isNoCredentialsFound
isTokenRenewFailed
等属性

Related Skills

相关技能

Quick Reference

快速参考

APIPurpose
Auth0(domain, clientId)
Create the SDK client
auth0.webAuthentication().login(...)
Launch Universal Login in the system browser
auth0.webAuthentication().logout()
Clear the browser session
auth0.webAuthentication(scheme: '...')
Use a custom URL scheme
auth0.credentialsManager.credentials()
Get stored credentials (auto-renews if expired)
auth0.credentialsManager.hasValidCredentials()
Check for a valid stored session
auth0.credentialsManager.clearCredentials()
Wipe stored credentials
LocalAuthentication(title: ...)
Enable biometric protection of stored credentials
API用途
Auth0(domain, clientId)
创建SDK客户端
auth0.webAuthentication().login(...)
在系统浏览器中启动Universal Login
auth0.webAuthentication().logout()
清除浏览器会话
auth0.webAuthentication(scheme: '...')
使用自定义URL scheme
auth0.credentialsManager.credentials()
获取存储的凭证(过期时自动刷新)
auth0.credentialsManager.hasValidCredentials()
检查是否存在有效的存储会话
auth0.credentialsManager.clearCredentials()
删除存储的凭证
LocalAuthentication(title: ...)
启用存储凭证的生物识别保护

References

参考链接