auth0-flutter-web

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Auth0 Flutter Web Integration

Auth0 Flutter Web 集成

auth0_flutter
is the official Auth0 SDK for Flutter applications. On the web platform, it wraps the Auth0 SPA JS SDK to provide browser-based authentication via redirect or popup flows with built-in credential caching and automatic token renewal.
Agent instruction: The current SDK version is
2.1.0
. Use this version in all dependency lines below. If you need to verify, run
flutter pub add auth0_flutter
which will resolve the latest compatible version automatically.
auth0_flutter
是Auth0官方推出的Flutter应用SDK。在Web平台上,它封装了Auth0 SPA JS SDK,通过重定向或弹窗流程提供基于浏览器的身份认证功能,并内置凭证缓存和自动令牌续期机制。
Agent指令: 当前SDK版本为
2.1.0
。以下所有依赖项均使用此版本。如需验证,运行
flutter pub add auth0_flutter
,它会自动解析最新兼容版本。

When NOT to Use

不适用于以下场景

Prerequisites

前置条件

  • Flutter 3.24.0+
  • Dart 3.5.0+
  • 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: For Single Page Applications, the Auth0 Domain and Client ID are public configuration (not secrets). A SPA uses PKCE with no client secret, and these values ship in the browser bundle. Pass them directly to
Auth0Web(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: Proceed to Step 3 and use those values directly in the
    Auth0Web(...)
    constructor.
  • If no credentials are provided: Create the application with the Auth0 CLI — do NOT ask the user to create or configure an Auth0 application manually in the Dashboard. Always use the CLI path.
Follow Setup Guide — Auth0 Configuration for the pre-flight checks and the
auth0 apps create
command.
注意: 对于单页应用,Auth0域名和客户端ID属于公开配置(并非机密)。单页应用使用PKCE(授权码流程+证明密钥),无需客户端密钥,这些值会随浏览器包一起发布。直接将其传入
Auth0Web(domain, clientId)
即可——无需存储在环境变量中或隐藏。
Agent指令:
  • 如果用户的提示中已提供Auth0凭证(域名和客户端ID): 继续步骤3,直接在
    Auth0Web(...)
    构造函数中使用这些值。
  • 如果未提供凭证: 使用Auth0 CLI创建应用——不要让用户在控制台手动创建或配置Auth0应用。始终使用CLI方式。
遵循设置指南——Auth0配置进行预检检查,并执行
auth0 apps create
命令。

Step 3 — Add Auth0 SPA JS to index.html

步骤3 — 将Auth0 SPA JS添加至index.html

Agent instruction:
  1. Locate the web entry point:
    web/index.html
  2. Add the Auth0 SPA JS script tag inside
    <head>
    :
html
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.1/auth0-spa-js.production.js" defer></script>
If
web/index.html
does not exist, the user may need to run
flutter create . --platforms=web
to add web support.
Agent指令:
  1. 找到Web入口文件:
    web/index.html
  2. <head>
    标签内添加Auth0 SPA JS脚本标签:
html
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.1/auth0-spa-js.production.js" defer></script>
如果
web/index.html
不存在,用户可能需要运行
flutter create . --platforms=web
来添加Web支持。

Step 4 — Configure Callback URLs

步骤4 — 配置回调URL

Agent instruction:
  1. Determine the app's web URL. For local development, default is
    http://localhost:3000
    (when running with
    --web-port 3000
    ).
  2. Ask the user via
    AskUserQuestion
    : "What port will you run your Flutter web app on locally? (default: 3000)"
  3. Register the callback URLs using the Auth0 CLI (substitute real values for
    CLIENT_ID
    ,
    APP_URL
    ):
bash
auth0 apps update CLIENT_ID \
  --callbacks "APP_URL" \
  --logout-urls "APP_URL" \
  --web-origins "APP_URL" \
  --no-input
For production, also add the production URL to each list.
Agent指令:
  1. 确定应用的Web URL。本地开发时,默认URL为
    http://localhost:3000
    (使用
    --web-port 3000
    运行时)。
  2. 通过
    AskUserQuestion
    询问用户:"你本地运行Flutter Web应用将使用哪个端口?(默认:3000)"
  3. 使用Auth0 CLI注册回调URL(将
    CLIENT_ID
    APP_URL
    替换为实际值):
bash
auth0 apps update CLIENT_ID \
  --callbacks "APP_URL" \
  --logout-urls "APP_URL" \
  --web-origins "APP_URL" \
  --no-input
对于生产环境,还需将生产URL添加至每个列表中。

Step 5 — Implement Authentication

步骤5 — 实现身份认证

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.
dart
// lib/auth_service.dart
import 'package:auth0_flutter/auth0_flutter.dart';
import 'package:auth0_flutter/auth0_flutter_web.dart';

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

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

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

  /// Call on app startup to restore session from cache
  Future<void> onLoad() async {
    _credentials = await _auth0.onLoad();
  }

  /// Redirect to Auth0 Universal Login
  Future<void> loginWithRedirect({String? redirectUrl}) async {
    await _auth0.loginWithRedirect(
      redirectUrl: redirectUrl,
      scopes: {'openid', 'profile', 'email', 'offline_access'},
    );
  }

  /// Open Auth0 login in a popup window
  Future<void> loginWithPopup() async {
    _credentials = await _auth0.loginWithPopup(
      scopes: {'openid', 'profile', 'email', 'offline_access'},
    );
  }

  /// Get cached credentials (auto-refreshes if expired)
  Future<Credentials> getCredentials() async {
    final creds = await _auth0.credentials();
    _credentials = creds;
    return creds;
  }

  /// Check if valid credentials exist
  Future<bool> hasValidCredentials() async {
    return await _auth0.hasValidCredentials();
  }

  /// Logout and redirect back to the app
  Future<void> logout({String? returnToUrl}) async {
    await _auth0.logout(returnToUrl: returnToUrl);
    _credentials = null;
  }
}
dart
// lib/main.dart
import 'package:flutter/material.dart';
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.onLoad();
    setState(() => _isLoading = false);
  }

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

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () => authService.loginWithRedirect(),
          child: const Text('Log In'),
        ),
      ),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    final user = authService.user;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        actions: [
          IconButton(
            onPressed: () => authService.logout(
              returnToUrl: Uri.base.origin,
            ),
            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
组件并相应更新。
dart
// lib/auth_service.dart
import 'package:auth0_flutter/auth0_flutter.dart';
import 'package:auth0_flutter/auth0_flutter_web.dart';

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

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

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

  /// 应用启动时调用,从缓存恢复会话
  Future<void> onLoad() async {
    _credentials = await _auth0.onLoad();
  }

  /// 重定向至Auth0通用登录页面
  Future<void> loginWithRedirect({String? redirectUrl}) async {
    await _auth0.loginWithRedirect(
      redirectUrl: redirectUrl,
      scopes: {'openid', 'profile', 'email', 'offline_access'},
    );
  }

  /// 在弹窗窗口中打开Auth0登录页面
  Future<void> loginWithPopup() async {
    _credentials = await _auth0.loginWithPopup(
      scopes: {'openid', 'profile', 'email', 'offline_access'},
    );
  }

  /// 获取缓存凭证(过期时自动刷新)
  Future<Credentials> getCredentials() async {
    final creds = await _auth0.credentials();
    _credentials = creds;
    return creds;
  }

  /// 检查是否存在有效凭证
  Future<bool> hasValidCredentials() async {
    return await _auth0.hasValidCredentials();
  }

  /// 登出并重定向回应用
  Future<void> logout({String? returnToUrl}) async {
    await _auth0.logout(returnToUrl: returnToUrl);
    _credentials = null;
  }
}
dart
// lib/main.dart
import 'package:flutter/material.dart';
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.onLoad();
    setState(() => _isLoading = false);
  }

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

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () => authService.loginWithRedirect(),
          child: const Text('登录'),
        ),
      ),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    final user = authService.user;
    return Scaffold(
      appBar: AppBar(
        title: const Text('首页'),
        actions: [
          IconButton(
            onPressed: () => authService.logout(
              returnToUrl: Uri.base.origin,
            ),
            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('欢迎,${user?.name ?? '用户'}!'),
            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';
import 'package:auth0_flutter/auth0_flutter_web.dart';

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

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

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

  Future<void> init() async {
    _credentials = await _auth0.onLoad();
    _isLoading = false;
    notifyListeners();
  }

  Future<void> loginWithRedirect() async {
    await _auth0.loginWithRedirect(
      scopes: {'openid', 'profile', 'email', 'offline_access'},
    );
  }

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

  Future<void> logout() async {
    await _auth0.logout(returnToUrl: Uri.base.origin);
    _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, 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';
import 'package:auth0_flutter/auth0_flutter_web.dart';

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

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

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

  Future<void> init() async {
    _credentials = await _auth0.onLoad();
    _isLoading = false;
    notifyListeners();
  }

  Future<void> loginWithRedirect() async {
    await _auth0.loginWithRedirect(
      scopes: {'openid', 'profile', 'email', 'offline_access'},
    );
  }

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

  Future<void> logout() async {
    await _auth0.logout(returnToUrl: Uri.base.origin);
    _credentials = null;
    notifyListeners();
  }
}
dart
// lib/main.dart — 用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 6 — Verify Build

步骤6 — 验证构建

Agent instruction: Run a build to verify the integration compiles without errors:
bash
flutter build web
Then run the app locally to test:
bash
flutter run -d chrome --web-port 3000
If the build fails, review error messages and fix up to 5 times before asking the user.
Agent指令: 运行构建命令以验证集成编译无错误:
bash
flutter build web
然后在本地运行应用进行测试:
bash
flutter run -d chrome --web-port 3000
如果构建失败,查看错误信息并尝试修复最多5次后再询问用户。

Detailed Documentation

详细文档

  • Setup Guide — Auth0 application creation via the Auth0 CLI, web/index.html setup, callback URL registration
  • Integration Patterns — Redirect login, popup login, credential management, state management patterns, organization support, error handling
  • API Reference & Testing — Full API reference, configuration options, claims reference, testing checklist, troubleshooting
  • 设置指南 — 通过Auth0 CLI创建Auth0应用、配置web/index.html、注册回调URL
  • 集成模式 — 重定向登录、弹窗登录、凭证管理、状态管理模式、组织支持、错误处理
  • API参考与测试 — 完整API参考、配置选项、声明参考、测试清单、故障排除

Common Mistakes

常见错误

MistakeFix
Auth0 app type not set to Single Page ApplicationIn Auth0 Dashboard, select "Single Page Application" when creating the application
Missing Auth0 SPA JS script in
web/index.html
Add
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.1/auth0-spa-js.production.js" defer></script>
to
<head>
Not calling
onLoad()
on app startup
Always call
onLoad()
in
initState()
or equivalent to restore sessions after redirect
Missing
Allowed Web Origins
in Auth0 Dashboard
Add your app URL (e.g.,
http://localhost:3000
) to Allowed Web Origins — required for silent token renewal
Using
auth0_flutter
mobile API on web
Import both
package:auth0_flutter/auth0_flutter.dart
(for types like
Credentials
,
UserProfile
) AND
package:auth0_flutter/auth0_flutter_web.dart
(for
Auth0Web
class)
Missing base import causes type errors
Credentials
and
UserProfile
are exported from
auth0_flutter.dart
, not
auth0_flutter_web.dart
— you need both imports
Callback URL mismatchEnsure
Allowed Callback URLs
matches the exact URL where your app runs (including port)
Not adding
--web-port
when running locally
Use
flutter run -d chrome --web-port 3000
to ensure consistent port matching callback URLs
Popup blocked by browser
loginWithPopup()
must be called from a direct user interaction (button click); cannot be called from
initState()
Missing
offline_access
scope
Add
'offline_access'
to scopes set to enable refresh token rotation for silent renewal
CORS errors on token endpointEnsure
Allowed Web Origins
is configured in Auth0 Dashboard (not just Callback URLs)
错误修复方案
Auth0应用类型未设置为单页应用在Auth0控制台创建应用时选择“单页应用”
web/index.html
中缺少Auth0 SPA JS脚本
<head>
中添加
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.1/auth0-spa-js.production.js" defer></script>
应用启动时未调用
onLoad()
始终在
initState()
或等效方法中调用
onLoad()
,以在重定向后恢复会话
Auth0控制台中缺少“允许的Web来源”将你的应用URL(例如
http://localhost:3000
)添加至“允许的Web来源”——这是静默令牌续期所必需的
在Web平台使用
auth0_flutter
移动端API
同时导入
package:auth0_flutter/auth0_flutter.dart
(用于
Credentials
UserProfile
等类型)和
package:auth0_flutter/auth0_flutter_web.dart
(用于
Auth0Web
类)
缺少基础导入导致类型错误
Credentials
UserProfile
auth0_flutter.dart
导出,而非
auth0_flutter_web.dart
——你需要同时导入这两个包
回调URL不匹配确保“允许的回调URL”与应用运行的精确URL(包括端口)一致
本地运行时未添加
--web-port
参数
使用
flutter run -d chrome --web-port 3000
确保端口与回调URL一致
弹窗被浏览器阻止
loginWithPopup()
必须从用户直接交互(如按钮点击)中调用;不能从
initState()
中调用
缺少
offline_access
权限
在权限集合中添加
'offline_access'
以启用刷新令牌轮换,实现静默续期
令牌端点出现CORS错误确保在Auth0控制台中配置了“允许的Web来源”(不仅仅是回调URL)

References

参考链接