mcpField
A
auth0-flutter-native

auth0-flutter-native

github.com/auth0セキュリティ55245 回インストール1 日前に更新
リポジトリ

auth0-flutter-native について

iOS と Android の Flutter アプリに Auth0 のネイティブ認証を組み込むスキルです。システムブラウザーで認証し、プラットフォームの安全なストレージで認証情報を管理します。生体認証による保護も利用できます。

主な機能

  1. 01公式 auth0_flutter SDK の統合
  2. 02システムブラウザーによる認証
  3. 03CredentialsManager によるトークン保存
  4. 04セッション復元と認証情報の更新
  5. 05生体認証による認証情報の保護

活用シーン

  1. 01Flutter モバイルアプリへの Auth0 ログイン追加
  2. 02認証済みセッションの保存と復元
  3. 03保存した認証情報の生体認証保護

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/auth0/agent-skills --skill auth0-flutter-native

インストール後、Claude Code で/auth0-flutter-nativeを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
api.md
integration.md
setup.md
tests/
benchmark-config.json
evals.json
graders.json
graders.ts
package.json
run-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 などを確認します

関連するスキル

クイックリファレンス

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: ...)保存済みの認証情報に対する生体認証保護を有効にする

参考資料

コメント