auth0-flutter-web について
auth0-flutter-webは、Flutter Web向けのAuth0連携スキルです。SDKの設定とブラウザー認証を案内し、リダイレクトやポップアップでのログイン、認証情報のキャッシュ、トークン更新を扱います。
主な機能
- 01Auth0 Flutter SDKの設定
- 02ブラウザーのリダイレクトとポップアップログイン
- 03認証情報のキャッシュとトークン更新
- 04状態管理パターンへの適応
- 05ビルド確認とトラブルシューティング
活用シーン
- 01Flutter WebアプリへのAuth0ログイン追加
- 02既存の状態管理に合わせた認証連携
- 03コールバックURLとWebオリジンの設定
- 04ビルドやトークン更新の問題確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ auth0/ agent-skills --skill auth0-flutter-webインストール後、Claude Code で/auth0-flutter-webを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api.mdintegration.mdsetup.mdtests/benchmark-config.jsonevals.jsongraders.jsongraders.tspackage.jsonrun-evals.mjs詳細
Auth0 Flutter Web インテグレーション
auth0_flutter は Flutter アプリケーション向けの公式 Auth0 SDK です。web プラットフォームでは、Auth0 SPA JS SDK をラップし、組み込みの認証情報キャッシュとトークンの自動更新機能を備えた、リダイレクトまたはポップアップフローによるブラウザベースの認証を提供します。
エージェントへの指示: 現在の SDK バージョンは
2.1.0です。以下のすべての依存関係の行では、このバージョンを使用してください。確認する必要がある場合は、flutter pub add auth0_flutterを実行すると、互換性のある最新バージョンが自動的に解決されます。
使用しないケース
- Flutter モバイル(iOS/Android):auth0-flutter-native を使用してください。モバイルでは、システムブラウザーを使った Web 認証を備える別のプラットフォームインターフェイスを使用します
- ネイティブ iOS(Swift):auth0-swift を使用してください
- ネイティブ Android(Kotlin/Java):auth0-android を使用してください
- React SPA:auth0-react を使用してください
- Angular SPA:auth0-angular を使用してください
- Vue SPA:auth0-vue を使用してください
- Node.js/Express サーバー:auth0-express を使用してください
前提条件
クイックスタートのワークフロー
エージェントへの指示: 以下の手順を順番に実行してください。どの手順でもエラーが発生した場合は、
AskUserQuestionを呼び出してユーザーに案内を求める前に、最大 5 回まで修正を試みてください。必ず最初に既存のコードを検索してください。ログイン/ログアウトのハンドラーがすでにある場合は、新しく作成せず、そのハンドラーに組み込んでください。
ステップ 1 — SDK をインストールする
エージェントへの指示: プロジェクトディレクトリに
pubspec.yamlがあるか確認してください。存在する場合は、依存関係を追加してください。見つからない場合は Flutter プロジェクトではないため、ユーザーに確認してください。プロジェクトのルートで実行します:
flutter pub add auth0_flutter
pubspec.yamlに依存関係が追加されたことを確認します:dependencies: auth0_flutter: ^2.1.0
ステップ 2 — Auth0 を設定する
注: シングルページアプリケーションでは、Auth0 ドメインとクライアント ID は公開設定値(シークレットではありません)です。SPA はクライアントシークレットを使わずに PKCE を使用し、これらの値はブラウザーバンドルに含まれます。
Auth0Web(domain, clientId)に直接渡してください。環境変数に保存したり、値を隠したりする必要はありません。エージェントへの指示:
- Auth0 の認証情報(ドメインとクライアント ID の両方)がユーザーのプロンプトにすでに含まれている場合: ステップ 3 に進み、
Auth0Web(...)コンストラクターでそれらの値を直接使用してください。- 認証情報が提供されていない場合: Auth0 CLI でアプリケーションを作成してください。ユーザーにダッシュボードで Auth0 アプリケーションを手動で作成または設定するよう依頼してはいけません。必ず CLI を使用してください。
事前確認と
auth0 apps createコマンドについては、セットアップガイド — Auth0 の設定に従ってください。
ステップ 3 — index.html に Auth0 SPA JS を追加する
エージェントへの指示:
- Web のエントリーポイントである
web/index.htmlを見つけます<head>内に Auth0 SPA JS のスクリプトタグを追加します:<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を実行する必要がある可能性があります。
ステップ 4 — コールバック URL を設定する
エージェントへの指示:
- アプリの Web URL を特定します。ローカル開発でのデフォルトは
http://localhost:3000です(--web-port 3000を指定して実行する場合)。AskUserQuestionを使ってユーザーに質問します:"Flutter Web アプリをローカルで実行するポートを教えてください(デフォルト:3000)"- Auth0 CLI を使ってコールバック URL を登録します(
CLIENT_IDとAPP_URLを実際の値に置き換えます):auth0 apps update CLIENT_ID \ --callbacks "APP_URL" \ --logout-urls "APP_URL" \ --web-origins "APP_URL" \ --no-input本番環境では、本番 URL も各リストに追加してください。
ステップ 5 — 認証を実装する
エージェントへの指示: プロジェクト内でメインアプリのエントリーポイント(
main.dart)を検索します。状態管理の方式を特定してください。
provider、riverpod、bloc、GetX、またはmobxのインポートを確認します- いずれも見つからない場合は、
setStateを使った基本的なStatefulWidgetを使用します次に、以下の該当するパスだけに従ってください。判断が難しい場合は、
AskUserQuestionを使って次のように質問してください。「Flutter アプリではどの状態管理方式を使用していますか? Provider、Riverpod、Bloc、または基本的な setState のいずれですか?」
基本的な StatefulWidget(デフォルト)
エージェントへの指示:
AuthServiceクラスを作成し、アプリのルートウィジェットに組み込みます。MaterialAppまたはCupertinoAppウィジェットを検索し、適宜更新してください。
// 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;
}
}
// 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});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _authService = AuthService(
domain: 'YOUR_AUTH0_DOMAIN',
clientId: 'YOUR_AUTH0_CLIENT_ID',
);
bool _isLoading = true;
@override
void initState() {
super.initState();
_initAuth();
}
Future<void> _initAuth() async {
await _authService.onLoad();
setState(() => _isLoading = false);
}
@override
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});
@override
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});
@override
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 ?? ''),
],
),
),
);
}
}
Provider(状態管理)
エージェントへの指示: プロジェクトで
providerを使用している場合は、AuthServiceをChangeNotifierとして作成し、アプリのルートでChangeNotifierProviderを使って注入します。
// 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();
}
}
// 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、高度なシナリオの完全なパターンについては、Integration Patterns を参照してください。
ステップ 6 — ビルドを検証する
エージェントへの指示: ビルドを実行して、エラーなく統合がコンパイルされることを確認します。
flutter build web次に、アプリをローカルで実行してテストします。
flutter run -d chrome --web-port 3000ビルドに失敗した場合は、エラーメッセージを確認して修正します。ユーザーに尋ねる前に、最大 5 回まで修正を試みてください。
詳細ドキュメント
- セットアップガイド — Auth0 CLI による Auth0 アプリケーションの作成、web/index.html の設定、コールバック URL の登録
- 統合パターン — リダイレクトログイン、ポップアップログイン、認証情報の管理、状態管理パターン、組織サポート、エラー処理
- API リファレンスとテスト — API リファレンス全般、設定オプション、クレームリファレンス、テストチェックリスト、トラブルシューティング
よくある間違い
| 間違い | 修正方法 |
|---|---|
| Auth0 アプリの種類が Single Page Application に設定されていない | Auth0 Dashboard でアプリケーションを作成する際に、「Single Page Application」を選択します |
web/index.html に Auth0 SPA JS スクリプトがない | <script src="https://cdn.auth0.com/js/auth0-spa-js/2.1/auth0-spa-js.production.js" defer></script> を <head> に追加します |
アプリの起動時に onLoad() を呼び出していない | リダイレクト後にセッションを復元できるよう、必ず initState() または同等の箇所で onLoad() を呼び出します |
Auth0 Dashboard に Allowed Web Origins が設定されていない | アプリの URL(例:http://localhost:3000)を Allowed Web Origins に追加します。サイレントトークン更新に必要です |
Web で auth0_flutter のモバイル API を使用している | package:auth0_flutter/auth0_flutter.dart(Credentials、UserProfile などの型用)と package:auth0_flutter/auth0_flutter_web.dart(Auth0Web クラス用)の両方をインポートします |
| ベースとなるインポートがなく、型エラーが発生する | Credentials と UserProfile は auth0_flutter_web.dart ではなく auth0_flutter.dart からエクスポートされています。両方のインポートが必要です |
| コールバック URL が一致しない | Allowed Callback URLs がアプリの実行 URL と完全に一致していること(ポートを含む)を確認します |
ローカルでの実行時に --web-port を指定していない | コールバック URL のポートを確実に一致させるため、flutter run -d chrome --web-port 3000 を使用します |
| ブラウザーでポップアップがブロックされる | loginWithPopup() はボタンクリックなど、ユーザーの直接操作から呼び出す必要があります。initState() からは呼び出せません |
offline_access スコープがない | サイレント更新でリフレッシュトークンのローテーションを有効にするには、スコープに 'offline_access' を追加します |
| トークンエンドポイントで CORS エラーが発生する | Auth0 Dashboard で Allowed Web Origins が設定されていることを確認します(Callback URLs だけでは不十分です) |