auth0-flutter-native について
iOS と Android の Flutter アプリに Auth0 のネイティブ認証を組み込むスキルです。システムブラウザーで認証し、プラットフォームの安全なストレージで認証情報を管理します。生体認証による保護も利用できます。
主な機能
- 01公式 auth0_flutter SDK の統合
- 02システムブラウザーによる認証
- 03CredentialsManager によるトークン保存
- 04セッション復元と認証情報の更新
- 05生体認証による認証情報の保護
活用シーン
- 01Flutter モバイルアプリへの Auth0 ログイン追加
- 02認証済みセッションの保存と復元
- 03保存した認証情報の生体認証保護
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ auth0/ agent-skills --skill auth0-flutter-nativeインストール後、Claude Code で/auth0-flutter-nativeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api.mdintegration.mdsetup.mdtests/benchmark-config.jsonevals.jsongraders.jsongraders.tspackage.jsonrun-evals.mjs詳細
Auth0 Flutter ネイティブ(iOS/Android)連携
auth0_flutter は Flutter アプリケーション向けの公式 Auth0 SDK です。**モバイル(iOS/Android)**では、デバイスのシステムブラウザー(Web 認証/Universal Login)を通じて認証を行い、組み込みの CredentialsManager を使ってトークンをプラットフォームのキーチェーン/キーストアに安全に保存します。生体認証による保護も任意で利用できます。
エージェントへの指示: SDK のセットアップ手順を案内する前に、次のいずれかを実行して最新のリリースバージョンを取得してください。
gh api repos/auth0/auth0-flutter/releases/latest --jq '.tag_name'flutter pub info auth0_flutter 2>/dev/null | head -5または pub.dev を確認してください。
curl -s https://pub.dev/api/packages/auth0_flutter | python3 -c "import sys,json;print(json.load(sys.stdin)['latest']['version'])"以下の依存関係の記述では、ハードコードされたバージョンではなく、取得したバージョンを使用してください。現在把握しているバージョン:
2.1.0。
使用しないケース
- Flutter Web:auth0-flutter-web を使用してください。Web では、Auth0 SPA JS をラップする別のプラットフォームインターフェイス(
Auth0Web)を使用します。 - ネイティブ iOS(Flutter なし、Swift):auth0-swift を使用してください。
- ネイティブ Android(Flutter なし、Kotlin/Java):auth0-android を使用してください。
- React Native:auth0-react-native を使用してください。
- React SPA:auth0-react を使用してください。
- Node.js/Express サーバー:auth0-express を使用してください。
前提条件
- 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 アプリケーションの作成と設定に使用)
クイックスタートの手順
エージェントへの指示: これらの手順を順番に実行してください。いずれかの手順でエラーが発生した場合は、ユーザーに案内を求めるため
AskUserQuestionを呼び出す前に、最大 5 回まで修正を試みてください。必ず先に既存のコードを検索してください。既存のログイン/ログアウトハンドラーがある場合は、新しく作成するのではなく、既存のものに接続してください。
ステップ 1 — SDK をインストールする
エージェントへの指示: プロジェクトディレクトリに
pubspec.yamlがあるか確認してください。存在する場合は、依存関係を追加してください。見つからない場合は Flutter プロジェクトではないため、ユーザーに確認してください。プロジェクトのルートで実行します。
flutter pub add auth0_flutter
pubspec.yamlに依存関係が追加されたことを確認します。dependencies: auth0_flutter: ^2.1.0
ステップ 2 — Auth0 を設定する
注: Auth0 Domain と Client ID は公開設定情報であり、シークレットではありません。ネイティブアプリではクライアントシークレットを使わず、PKCE を使用します。これらの値は
Auth0(domain, clientId)に直接渡してください。環境変数に保存したり、隠したりする必要はありません。エージェントへの指示:
- ユーザーのプロンプトに Auth0 の認証情報(ドメインとクライアント ID の両方)がすでに記載されている場合: その値を
Auth0(...)コンストラクターで直接使用し、ステップ 3 に進んでください。- 認証情報が提供されていない場合:
AskUserQuestionを使用し、希望するセットアップ方法をユーザーに尋ねてください。「Auth0 アプリケーションはどのようにセットアップしますか? 自動(Auth0 CLI を実行して作成する)と手動(Auth0 Dashboard で作成し、Domain と Client ID を教えていただく)から選んでください。」
- 自動: セットアップガイドの Auth0 CLI の手順に従い、ネイティブアプリケーションを作成してください。
- 手動: ユーザーに Auth0 Domain と Client ID を尋ね、それらを直接使用してください。
事前チェックと
auth0 apps createコマンドについては、セットアップガイド — Auth0 の設定に従ってください。
ステップ 3 — Android を設定する
エージェントへの指示:
android/app/build.gradle(またはbuild.gradle.kts)を編集し、android { defaultConfig { ... } }の中にmanifestPlaceholdersを追加してください。これらは、SDK のRedirectActivityインテントフィルターが登録するコールバック URL を指定します。これらがないと、Auth0 用にアプリを正しくビルドできません。
android/app/build.gradle(Groovy)の場合:
android {
defaultConfig {
manifestPlaceholders = [auth0Domain: "YOUR_AUTH0_DOMAIN", auth0Scheme: "https"]
}
}
android/app/build.gradle.kts(Kotlin DSL)の場合:
android {
defaultConfig {
manifestPlaceholders["auth0Domain"] = "YOUR_AUTH0_DOMAIN"
manifestPlaceholders["auth0Scheme"] = "https"
}
}
エージェントへの指示: Android App Links(推奨)を使用するには、
auth0Scheme: "https"を指定してください。アプリでカスタムスキームを使用する場合は、小文字のスキーム文字列を設定し、Dart のwebAuthentication(scheme: ...)に同じスキームを渡してください。詳細については、セットアップガイドを参照してください。
ステップ 4 — iOS を設定する
エージェントへの指示: iOS 17.4 以降でデフォルトの HTTPS(ユニバーサルリンク)フローを使用する場合、
Info.plistの変更は不要ですが、Xcode で Associated Domains 機能(webcredentials:YOUR_AUTH0_DOMAIN)を追加する必要があります。古い iOS またはカスタム URL スキームを使用する場合は、ios/Runner/Info.plistにCFBundleURLTypesエントリーを追加してください。
<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>
ステップ 5 — コールバック URL を設定する
エージェントへの指示: Auth0 CLI を使用して、プラットフォーム固有のコールバック URL とログアウト URL を登録してください。Android パッケージ名(
android/app/build.gradleのapplicationId)と iOS バンドル識別子(Xcode のPRODUCT_BUNDLE_IDENTIFIER)を確認し、以下のコマンドのプレースホルダー(CLIENT_ID、YOUR_DOMAIN、ANDROID_PACKAGE_NAME、IOS_BUNDLE_ID)をプロジェクトの値に置き換えて実行してください。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
ステップ 6 — 認証を実装する
エージェントへの指示: プロジェクト内で、アプリのメインエントリーポイント(
main.dart)を検索してください。状態管理の方法を特定してください。
provider、riverpod、bloc、GetX、mobxのインポートを探します- 見つからない場合は、
setStateを使用する基本的なStatefulWidgetを使用します次に、以下のうち該当する方法のみに従ってください。判断できない場合は、
AskUserQuestionを使って次のように質問してください。「Flutter アプリではどの状態管理方法を使用していますか?Provider、Riverpod、Bloc、または基本的な setState のどれですか?」
基本的な StatefulWidget(デフォルト)
エージェントへの指示:
AuthServiceクラスを作成し、アプリのルートウィジェットに接続してください。MaterialAppまたはCupertinoAppウィジェットを検索し、適宜更新してください。起動時に、CredentialsManagerのキャッシュからセッションを復元してください。
// 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;
}
}
// 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});
@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.init();
setState(() => _isLoading = false);
}
@override
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});
@override
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});
@override
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(状態管理)
エージェントへの指示: プロジェクトで
providerを使用している場合は、AuthServiceをChangeNotifierとして作成し、アプリのルートでChangeNotifierProviderを介して注入してください。
// 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();
}
}
// 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、生体認証、高度なシナリオを含む完全なパターンについては、統合パターンを参照してください。
ステップ 7 — ビルドを確認する
エージェントへの指示: ビルドを実行し、統合がエラーなくコンパイルされることを確認してください。
flutter build apk --debug # Android flutter build ios --no-codesign # iOS (on macOS)次に、デバイスまたはエミュレーターでアプリを実行してテストしてください。
flutter runビルドに失敗した場合は、エラーメッセージを確認して修正してください。ユーザーに尋ねる前に、最大 5 回まで試してください。
実機テスト: 生体認証(Face ID/Touch ID/指紋認証)はシミュレーター/エミュレーターではテストできません。iOS Simulator と Android エミュレーターでは、生体認証ハードウェアが制限されているか、利用できません。リリース前に、実際のデバイスで生体認証とユニバーサルログインへのリダイレクト全体をテストしてください。
詳細なドキュメント
- セットアップガイド — Auth0 CLI を使った Auth0 アプリケーションの作成、Android の
manifestPlaceholders、iOS のInfo.plist/Associated Domains、コールバック URL の登録 - 統合パターン — Web Auth のログイン/ログアウト、CredentialsManager、生体認証による保護、カスタムスキーム、組織、API アクセストークン、状態管理パターン、エラー処理
- API リファレンスとテスト — API リファレンス全般、設定オプション、クレームのリファレンス、テストチェックリスト、トラブルシューティング
よくある間違い
| 間違い | 修正方法 |
|---|---|
| Auth0 アプリのタイプが Native に設定されていない | auth0 apps create --type native を使ってアプリケーションを作成するか、Auth0 Dashboard で「Native」を選択します |
Android で manifestPlaceholders が設定されていない | android/app/build.gradle の defaultConfig に manifestPlaceholders = [auth0Domain: "...", auth0Scheme: "https"] を追加します。設定しないとビルドに失敗します |
モバイルで Auth0Web を使用している | モバイルでは Auth0Web(Web 専用 API)ではなく、webAuthentication() を使う Auth0 クラスを使用します |
モバイルで auth0_flutter_web.dart をインポートしている | package:auth0_flutter/auth0_flutter.dart のみをインポートします。_web のインポートは Flutter Web 用です |
| コールバック URL が一致していない | Allowed Callback URLs に https://YOUR_DOMAIN/android/PACKAGE_NAME/callback と https://YOUR_DOMAIN/ios/BUNDLE_ID/callback を登録します |
| Gradle と Dart でスキームが一致していない | auth0Scheme がカスタムスキームの場合は、webAuthentication(scheme: 'myscheme') に同じ値を渡します |
| Android で大文字を含むカスタムスキームを使用している | Android のカスタムスキームはすべて小文字である必要があります |
| Android で生体認証のプロンプトが表示されない | 生体認証のプロンプトを動作させるには、MainActivity が FlutterActivity ではなく FlutterFragmentActivity を継承する必要があります |
| ログイン後に認証情報を保存していない | webAuthentication().login() は認証情報を自動的に保存します。api.renewCredentials を使って手動で更新する場合を除き、再保存しないでください |
| 起動時にセッションを復元していない | initState() で credentialsManager.hasValidCredentials() と credentials() を呼び出してセッションを復元します |
offline_access スコープが設定されていない | 'offline_access' をスコープに追加すると、CredentialsManager がリフレッシュトークンを使って期限切れのアクセストークンをサイレント更新できます |
汎用の Exception を捕捉している | WebAuthenticationException(ログイン/ログアウト)と CredentialsManagerException(認証情報のエラー)を捕捉し、isUserCancelledException、isNoCredentialsFound、isTokenRenewFailed などを確認します |
関連するスキル
- auth0-quickstart — Auth0 の初期設定とアカウント作成
- auth0-flutter-web — 同じ SDK の Web プラットフォーム版
- auth0-swift — iOS ネイティブ(Swift)
- auth0-android — Android ネイティブ(Kotlin/Java)
クイックリファレンス
| API | 目的 |
|---|---|
Auth0(domain, clientId) | SDK クライアントを作成する |
auth0.webAuthentication().login(...) | システムブラウザーで Universal Login を起動する |
auth0.webAuthentication().logout() | ブラウザーセッションをクリアする |
auth0.webAuthentication(scheme: '...') | カスタム URL スキームを使用する |
auth0.credentialsManager.credentials() | 保存済みの認証情報を取得する(期限切れの場合は自動更新) |
auth0.credentialsManager.hasValidCredentials() | 有効な保存済みセッションがあるか確認する |
auth0.credentialsManager.clearCredentials() | 保存済みの認証情報を消去する |
LocalAuthentication(title: ...) | 保存済みの認証情報に対する生体認証保護を有効にする |