auth0-flutter-web
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAuth0 Flutter Web Integration
Auth0 Flutter Web 集成
auth0_flutterAgent instruction: The current SDK version is. Use this version in all dependency lines below. If you need to verify, run2.1.0which will resolve the latest compatible version automatically.flutter pub add auth0_flutter
auth0_flutterAgent指令: 当前SDK版本为。以下所有依赖项均使用此版本。如需验证,运行2.1.0,它会自动解析最新兼容版本。flutter pub add auth0_flutter
When NOT to Use
不适用于以下场景
- Flutter mobile (iOS/Android): Use auth0-flutter-native — mobile uses a different platform interface with Web Auth via system browser
- Native iOS (Swift): Use auth0-swift
- Native Android (Kotlin/Java): Use auth0-android
- React SPA: Use auth0-react
- Angular SPA: Use auth0-angular
- Vue SPA: Use auth0-vue
- Node.js/Express servers: Use auth0-express
- Flutter移动端(iOS/Android): 使用auth0-flutter-native——移动端采用不同的平台接口,通过系统浏览器实现Web身份认证
- 原生iOS(Swift): 使用auth0-swift
- 原生Android(Kotlin/Java): 使用auth0-android
- React单页应用: 使用auth0-react
- Angular单页应用: 使用auth0-angular
- Vue单页应用: 使用auth0-vue
- Node.js/Express服务器: 使用auth0-express
Prerequisites
前置条件
- Flutter 3.24.0+
- Dart 3.5.0+
- Auth0 account — Sign up free
- Auth0 CLI — install instructions (used to create and configure the Auth0 application)
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 callingto 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.AskUserQuestion
Agent指令: 按以下步骤依次操作。若在任何步骤遇到错误,尝试修复最多5次后,再调用向用户寻求指导。始终先搜索现有代码——如果已有登录/登出处理程序,应接入现有程序而非创建新程序。AskUserQuestion
Step 1 — Install SDK
步骤1 — 安装SDK
Agent instruction: Check the project directory for. If present, add the dependency. If not found, this is not a Flutter project — ask the user.pubspec.yamlRun in the project root:bashflutter pub add auth0_flutterVerify the dependency was added to:pubspec.yamlyamldependencies: auth0_flutter: ^2.1.0
Agent指令: 检查项目目录中是否存在。若存在,添加依赖项;若未找到,则这不是Flutter项目——请询问用户。pubspec.yaml在项目根目录运行:bashflutter pub add auth0_flutter验证依赖项已添加至:pubspec.yamlyamldependencies: 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— there is no need to store them in environment variables or hide them.Auth0Web(domain, clientId)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
constructor.Auth0Web(...)- 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 thecommand.auth0 apps create
注意: 对于单页应用,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:
- Locate the web entry point:
web/index.html- 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>Ifdoes not exist, the user may need to runweb/index.htmlto add web support.flutter create . --platforms=web
Agent指令:
- 找到Web入口文件:
web/index.html- 在
标签内添加Auth0 SPA JS脚本标签:<head>html<script src="https://cdn.auth0.com/js/auth0-spa-js/2.1/auth0-spa-js.production.js" defer></script>如果不存在,用户可能需要运行web/index.html来添加Web支持。flutter create . --platforms=web
Step 4 — Configure Callback URLs
步骤4 — 配置回调URL
Agent instruction:
- Determine the app's web URL. For local development, default is
(when running withhttp://localhost:3000).--web-port 3000- Ask the user via
: "What port will you run your Flutter web app on locally? (default: 3000)"AskUserQuestion- Register the callback URLs using the Auth0 CLI (substitute real values for
,CLIENT_ID):APP_URLbashauth0 apps update CLIENT_ID \ --callbacks "APP_URL" \ --logout-urls "APP_URL" \ --web-origins "APP_URL" \ --no-inputFor production, also add the production URL to each list.
Agent指令:
- 确定应用的Web URL。本地开发时,默认URL为
(使用http://localhost:3000运行时)。--web-port 3000- 通过
询问用户:"你本地运行Flutter Web应用将使用哪个端口?(默认:3000)"AskUserQuestion- 使用Auth0 CLI注册回调URL(将
、CLIENT_ID替换为实际值):APP_URLbashauth0 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 (). Determine the state management approach:main.dart
- Look for
,provider,riverpod,bloc, orGetXimportsmobx- If none found, use basic
withStatefulWidgetsetStateThen follow only the matching path below. If ambiguous, ask via: "Which state management approach does your Flutter app use — Provider, Riverpod, Bloc, or basic setState?"AskUserQuestion
Agent指令: 搜索项目的主应用入口文件()。确定状态管理方式:main.dart
- 查找
、provider、riverpod、bloc或GetX导入语句mobx- 如果未找到,使用基础的
配合StatefulWidgetsetState然后仅遵循以下匹配的流程。若存在歧义,通过询问用户:"你的Flutter应用使用哪种状态管理方式——Provider、Riverpod、Bloc还是基础setState?"AskUserQuestion
Basic StatefulWidget (Default)
基础StatefulWidget(默认)
Agent instruction: Create anclass, then wire it into the app's root widget. Search for theAuthServiceorMaterialAppwidget and update accordingly.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;
/// 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, createprovideras aAuthServiceand inject it viaChangeNotifierat the app root.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 — 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:bashflutter build webThen run the app locally to test:bashflutter run -d chrome --web-port 3000If the build fails, review error messages and fix up to 5 times before asking the user.
Agent指令: 运行构建命令以验证集成编译无错误:bashflutter build web然后在本地运行应用进行测试:bashflutter 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
常见错误
| Mistake | Fix |
|---|---|
| Auth0 app type not set to Single Page Application | In Auth0 Dashboard, select "Single Page Application" when creating the application |
Missing Auth0 SPA JS script in | Add |
Not calling | Always call |
Missing | Add your app URL (e.g., |
Using | Import both |
| Missing base import causes type errors | |
| Callback URL mismatch | Ensure |
Not adding | Use |
| Popup blocked by browser | |
Missing | Add |
| CORS errors on token endpoint | Ensure |
| 错误 | 修复方案 |
|---|---|
| Auth0应用类型未设置为单页应用 | 在Auth0控制台创建应用时选择“单页应用” |
| 在 |
应用启动时未调用 | 始终在 |
| Auth0控制台中缺少“允许的Web来源” | 将你的应用URL(例如 |
在Web平台使用 | 同时导入 |
| 缺少基础导入导致类型错误 | |
| 回调URL不匹配 | 确保“允许的回调URL”与应用运行的精确URL(包括端口)一致 |
本地运行时未添加 | 使用 |
| 弹窗被浏览器阻止 | |
缺少 | 在权限集合中添加 |
| 令牌端点出现CORS错误 | 确保在Auth0控制台中配置了“允许的Web来源”(不仅仅是回调URL) |