FlutterアプリケーションへのAuth 0の追加


私は、それがAuth 0でフラッターアプリケーションを認証するのがどれくらい簡単かについて調査したかったです.
この記事はAuthor 0を使用してフラッタアプリケーションに認証を追加したい人のためです.
私たちの結果は、社会的プロバイダと接続し、カスタムユーザープロファイルを返すアプリケーションになります.
その後、ログインフローを使用してアプリケーションを強化するためにこれを使用できます.

この記事を研究している間、私はすべての既存の記事が少し時代遅れであると結論しました.

我々のフラッターアプリに依存性を加えること


私たちはいくつかの外部パッケージを使用するので、私たちは私たちにそれらを追加する必要がありますpubspec.yaml ファイルを最初に.
dependencies:
  flutter:
    sdk: flutter
  http: ^0.12.1
  flutter_appauth: ^0.9.1
  flutter_secure_storage: ^3.3.3
3つの外部依存関係を使用します.
  • http : Auth 0エンドポイントにHTTPリクエストを作成するには
  • flutter_appauth : フラッタパッケージとブリッジフラッタとAuth 0
  • flutter_secure_storage : デバイス上の特定のトークンを保存できるパッケージ
  • 加えて、実行flutter pub get これらの依存関係を取得するには

    アプリケーションの作成


    このプロセスを続行するには、まずAuth 0アカウントを設定し、最初のアプリケーションを作成する必要があります.
    オーサ0と0に向かうcreate an account .
    一度あなたのアカウントを設定すると、最初のアプリケーションを作成します.
    左メニューの「アプリケーション」項目をクリックし、「Create Application」をクリックします.

    我々のフラッターアプリの統合のためのネイティブを選択します.
    一度設定タブを作成し、次の項目をコピーします.
  • ドメイン
  • クライアントID

  • この情報を使用して、フラッターのアプリケーションに頭を!

    基本的なAuth 0フラッターアプリを作成する


    さあ、へ移動しましょうmain.dart ファイルと我々はこのアプリのために必要なすべてのインポートを追加します.
    import 'dart:convert';
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    import 'package:flutter_appauth/flutter_appauth.dart';
    import 'package:flutter_secure_storage/flutter_secure_storage.dart';
    
    それから、我々もすべてを定義する必要がありますappAuth and secureStorage プロバイダ.
    final FlutterAppAuth appAuth = FlutterAppAuth();
    const FlutterSecureStorage secureStorage = FlutterSecureStorage();
    
    だけでなく、Auth 0の設定が必要です.
    const String AUTH0_ISSUER = '{YOUR_KEY}.us.auth0.com';
    const String AUTH0_DOMAIN = 'https://$AUTH0_ISSUER';
    const String AUTH0_CLIENT_ID = 'CLIENT_ID_HERE';
    const String AUTH0_REDIRECT_URI = 'com.{your domain}.{your app}://login-callback';
    
    次に、私たちのアプリケーションをレンダリングするだけで我々の主な機能を変更しましょう.
    void main() => runApp(const MyApp());
    
    MyAppは、いくつかの状態変数を保持するステイタスウィジェットです.
    class MyApp extends StatefulWidget {
      const MyApp({Key key}) : super(key: key);
    
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    この状態は私たちの主なエントリポイントになります.誰かがまだログインしていて、彼らがログインしないとき、彼らのプロフィールまたはログインページを示すならば、それは評価されます.
    class _MyAppState extends State<MyApp> {
      bool isBusy = false;
      bool isLoggedIn = false;
      String errorMessage;
      String name;
      String picture;
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Auth0 login',
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Flutter Auth0 login'),
            ),
            body: Center(
              child: isBusy
                  ? const CircularProgressIndicator()
                  : isLoggedIn
                      ? Profile(logoutAction, name, picture)
                      : Login(loginAction, errorMessage),
            ),
          ),
        );
      }
    }
    
    これは基本的な状態です.そして、我々が我々が第2で使用する若干の変数を定義します.だけでなく、ビルドウィジェットを動的に表示ウィジェットをレンダリングするには、ログイン状態に基づいて返します.
    我々はクリックボタンをクリックすると、アプリケーションが読み込まれている場合、意味は、ローディングインジケータが表示されます.
    次に、ログインしている人を区別し、プロファイルページを表示するか、ログインしていない人にログインページを提供します.
    これらの2つのウィジェットにプロパティがあることを発見したかもしれません.例えば、ログインはloginActionerrorMessage . これらは私たちがこのウィジェットに渡すという状態のプロパティです.
    始めにこれらの2ページを作りましょう.

    ログインページの作成


    ログインページに関してはloginActionerrorMessage . これらはこのウィジェットの中にマップされます.
    さらに、中心要素を持つウィジェットを返します.その中で、私たちは一度呼び出しで押されたボタンをレンダリングしますloginAction .
    エラーメッセージを表示できるテキスト要素を追加します.
    class Login extends StatelessWidget {
      final Future<void> Function() loginAction;
      final String loginError;
    
      const Login(this.loginAction, this.loginError, {Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: () async {
                await loginAction();
              },
              child: const Text('Login'),
            ),
            Text(loginError ?? ''),
          ],
        );
      }
    }
    

    Note: We'll get back to creating this loginAction function in a bit.


    プロファイルビューの作成


    プロフィールウィジェットを見てみましょう.
    class Profile extends StatelessWidget {
      final Future<void> Function() logoutAction;
      final String name;
      final String picture;
    
      const Profile(this.logoutAction, this.name, this.picture, {Key key})
          : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              width: 150,
              height: 150,
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blue, width: 4),
                shape: BoxShape.circle,
                image: DecorationImage(
                  fit: BoxFit.fill,
                  image: NetworkImage(picture ?? ''),
                ),
              ),
            ),
            const SizedBox(height: 24),
            Text('Name: $name'),
            const SizedBox(height: 48),
            ElevatedButton(
              onPressed: () async {
                await logoutAction();
              },
              child: const Text('Logout'),
            ),
          ],
        );
      }
    }
    

    これにより、ログインしたユーザのプロファイルイメージと名前、および現在のユーザをログアウトすることができるログアウトボタンをレンダリングします.
    これらのパラメータは、再び我々のアプリの状態ウィジェットから渡されます.
    それで、状態ウィジェットの上に戻って、これらのすべての機能を加えましょう.

    ログイン機能の作成


    これまで見たように、私たちはいくつかの関数を渡しました.loginAction and logoutAction .
    これらは_MyAppState 状態.
    これで_MyAppState , 我々はこれらの追加を開始します.
    我々が必要とする最初のものはloginAction , これは、auths 0フローを起動し、ユーザにログインするために呼び出される関数です.
    Future<void> loginAction() async {
        setState(() {
          isBusy = true;
          errorMessage = '';
        });
    
        try {
          final AuthorizationTokenResponse result =
              await appAuth.authorizeAndExchangeCode(
            AuthorizationTokenRequest(
              AUTH0_CLIENT_ID,
              AUTH0_REDIRECT_URI,
              issuer: AUTH0_DOMAIN,
              scopes: <String>['openid', 'profile', 'offline_access'],
            ),
          );
    
          final Map<String, Object> idToken = parseIdToken(result.idToken);
          final Map<String, Object> profile =
              await getUserDetails(result.accessToken);
    
          await secureStorage.write(
              key: 'refresh_token', value: result.refreshToken);
    
          setState(() {
            isBusy = false;
            isLoggedIn = true;
            name = idToken['name'];
            picture = profile['picture'];
          });
        } on Exception catch (e, s) {
          debugPrint('login error: $e - stack: $s');
    
          setState(() {
            isBusy = false;
            isLoggedIn = false;
            errorMessage = e.toString();
          });
        }
    }
    
    ここで何が起こるのかというと、まず我々の状態を忙しく設定し、エラーメッセージをリセットすることです.
    その後、我々はAppAuthプロバイダを使用して、Author 0アプリケーションからトークン要求を取得しようとします.
    この戻り値は、手動で解析する必要があるトークンですparseIdToken .
    一旦これが何かを返すならば、我々は関数と呼ばれるgetUserDetails . この関数は、アクセストークンに基づいてユーザデータを取得します.
    その後、我々はユーザーが戻ったり、現在のトークンが期限切れに使用するために我々のアプリケーションのストレージにリフレッシュトークンを設定します.
    最後に、ユーザーの名前とプロファイル画像を含む最後の状態を設定します.

    parseidToken関数の作成


    この関数は、Author 0で返されるトークンを受け取り、それを使うことができます.
    Map<String, Object> parseIdToken(String idToken) {
        final List<String> parts = idToken.split('.');
        assert(parts.length == 3);
    
        return jsonDecode(
            utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))));
    }
    
    これはドットに基づいてトークンを分割します.これはJWTトークンとして3つの部分を与えるべきです.
    次に、アクセストークンとして使用できる最初の部分をデコードします.

    GetUserDetail関数の追加


    上記のように、関数と呼ばれる関数を呼び出しますgetUserDetails . この関数は、Auth 0システムを問い合わせると、現在のユーザーの詳細を求めます.
    Future<Map<String, Object>> getUserDetails(String accessToken) async {
        const String url = '$AUTH0_DOMAIN/userinfo';
        final http.Response response = await http.get(
          url,
          headers: <String, String>{'Authorization': 'Bearer $accessToken'},
        );
    
        if (response.statusCode == 200) {
          return jsonDecode(response.body);
        } else {
          throw Exception('Failed to get user details');
        }
    }
    
    この関数はuser info エンドポイントで、ログインしているユーザの基本的な情報を返します.
    応答オブジェクトをログインアクションに返します.
    オブジェクトは以下のようになります.
    {
      "sub": "google-oauth2|103639452207137831992",
      "given_name": "Chris",
      "family_name": "Bongers",
      "nickname": "chrisbongers",
      "name": "Chris Bongers",
      "picture": "https://lh3.googleusercontent.com/a-/AOh14GhFAn4wXcc8l-7S97xbxHsw_ByihAPniCvy_kpZyKM=s96-c",
      "locale": "en-GB",
      "updated_at": "2021-08-15T08:27:32.042Z"
    }
    

    ログアウト関数の作成


    ログアウト機能は、ローカルに格納されたトークンと現在の状態を削除する必要がありますので、非常に簡単です.
    Future<void> logoutAction() async {
        await secureStorage.delete(key: 'refresh_token');
        setState(() {
          isLoggedIn = false;
          isBusy = false;
        });
    }
    

    フラッタAutos 0統合の仕上げ


    これで、我々は我々が必要とするすべてを持っています、しかし、我々のアプリはデフォルトのinitを持ちません.
    これは現在のユーザトークンを持っているかどうかをチェックし、ログインコールを継続する必要があります.
    先に行き、このinitを作りましょう.
    @override
    void initState() {
        initAction();
        super.initState();
    }
    
    Future<void> initAction() async {
        final String storedRefreshToken =
            await secureStorage.read(key: 'refresh_token');
        if (storedRefreshToken == null) return;
    
        setState(() {
          isBusy = true;
        });
    
        try {
          final TokenResponse response = await appAuth.token(TokenRequest(
            AUTH0_CLIENT_ID,
            AUTH0_REDIRECT_URI,
            issuer: AUTH0_ISSUER,
            refreshToken: storedRefreshToken,
          ));
    
          final Map<String, Object> idToken = parseIdToken(response.idToken);
          final Map<String, Object> profile =
              await getUserDetails(response.accessToken);
    
          await secureStorage.write(
              key: 'refresh_token', value: response.refreshToken);
    
          setState(() {
            isBusy = false;
            isLoggedIn = true;
            name = idToken['name'];
            picture = profile['picture'];
          });
        } on Exception catch (e, s) {
          debugPrint('error on refresh token: $e - stack: $s');
          await logoutAction();
        }
    }
    
    我々は、アプリケーションが開くたびに呼び出される独自のinit関数を作成しました.
    これが起こると、私たちは私たちのストレージにトークンがあるかどうかを確認します.
    このトークンを使用して、新しいトークンを要求し、保存したものを更新します.さらに、我々はgetUserDetails 機能と我々の状態を更新します.
    これは、ログイン機能で何をしているかに非常に近いので、よく知らなければなりません.
    それで、我々は我々の全体の流れを終えました.
    我々は今サインアップし、我々のフラッターアプリケーションのアカウントを作成するだけでなく、サインアウトすることができます.
    クールな部分:
    我々は我々のアプリケーションに戻ると、我々は再びログインし、トークンをリフレッシュされます.
    あなたは完全なコードを探しているかもしれません.私はGitHub repo ここでは、完全なコード例のアプリを見ることができます.

    読んでいただきありがとうございます、接続しましょう!


    私のブログを読んでくれてありがとう.私の電子メール会報を購読して、接続してくださいFacebook or