FlutterアプリにCognitoのソーシャルサインインを実装する【Google編】

はじめに

ソフトウェアエンジニアの谷藤です。

今回は Flutter アプリに Amazon Cognito を使って、Google のソーシャルサインインを実装する手順について解説していきます。

ソーシャルサインインをメインに解説していきたいので、Cognito 自体についてやユーザープールの解説はしません。

aws.amazon.com

ユーザープールの設定

まずはユーザープールの設定をします。

1. ユーザープールを作成する

まずはユーザープールを作成します。各項目についてはご自身の好みや作っているアプリによって適切に設定してください。

サインインの方法(メアドか電話番号かユーザー名のどれを使うかなど)や必須の標準属性については、作成後に変更ができないので、この点を注意して設定してください。

f:id:HiroTanifuji:20211001202722p:plain

2. ユーザープールにクライアントアプリを追加する

次に全般設定の「アプリクライアント」タブからユーザープールにクライアントアプリを追加します。

クライアントシークレット以外は後から変更可能なので、各項目は気にせずチェックにしちゃって良いと思います。

クライアントシークレットはご自身の環境で必要な場合のみ、チェックを入れてください。

f:id:HiroTanifuji:20211001204145p:plain

3. Cognito ドメインを作成する

次にアプリの統合の「ドメイン名」タブからドメインプレフィックスを設定して、Cognitoで使うドメインを作成してください。

f:id:HiroTanifuji:20211001212026p:plain

この後の設定でも使うので、作成したドメインをメモしておきます。

Google Cloud Platform の設定

次に Google Cloud Platform の設定をします。

1. プロジェクトを作成する

ユーザープールには一つの GCP のプロジェクト(アプリ)しか紐付けることができないので、この辺りも考慮しつつ進めてください。

2. アプリを登録する

API とサービス > OAuth 同意画面 からアプリを登録します。

社内向けのアプリ以外は外部のユーザーに使ってもらうと思うので、基本的には User Type は外部を選択してください。

アプリ名やアプリのロゴなどはご自身環境に合わせて設定してください。

スコープについてはユーザープールが Google から受け取りたい値を設定します。基本的には email は必要だと思うので、設定します。

f:id:HiroTanifuji:20211001210858p:plain

3. OAuth クライアント ID を作成する

次に OAuth のクライアント ID を作成します。

API とサービス > 認証情報 > 認証情報を作成 から OAuth クライアント ID を選択します。

f:id:HiroTanifuji:20211001211310p:plain

アプリケーションの種類はウェブ アプリケーション を選択します。

承認済みのリダイレクト URI にはこちらで作成した Cognito のドメイン'/oauth2/idpresponse' を付け足した URI を設定します。

f:id:HiroTanifuji:20211001213836p:plain

作成が完了すると、クライアント ID とクライアントシークレットが表示されます。この後に使うのでメモしておきます。

f:id:HiroTanifuji:20211001212709p:plain

ユーザープールでソーシャルサインインの設定

ユーザープールに戻って、ソーシャルサインインのための設定をしていきます。

1. ID プロバイダとして Google を追加する

フェデレーションの「ID プロバイダ」タブから Google を選択して、上でメモしたクライアント ID とクライアントシークレットを入力します。

Google アプリ ID に クライアント ID を、アプリシークレット にクライアントシークレットを入力してください。

認証スコープは、ユーザープールが Google から受け取りたい値を設定してください。

f:id:HiroTanifuji:20211001213309p:plain

その後、忘れずに属性マッピングの設定をしてください。

これを忘れると、Google から受け取った email などの値が、ユーザープールのユーザーの email とマッピングされず、アプリケーションで利用することができません。

f:id:HiroTanifuji:20211001213527p:plain

2. アプリクライアントの設定をする

次に、アプリの統合の「アプリクライアントの設定」タブから上で作成したアプリクライアントの詳細設定をしていきます。

まず、有効な ID プロバイダとして Google を選択してください。ユーザープールで設定したサインイン方法(メアド + パスワードなど)も有効にしたい場合は選択します。

サインインとサインアウトの URL を設定します。

それぞれ何でも良いと思いますが、今回は公式が解説している通り、アプリ名 + '://' を設定します。

https://docs.amplify.aws/lib/auth/signin_web_ui/q/platform/flutter/

OAuth 2.0 の設定で Authorization code grant を有効にする必要があります。

許可されている OAuth スコープにはご自身の環境に合わせて設定してほしいですが、無難に全て選択してしまっても問題ないと思います。

f:id:HiroTanifuji:20211001220904p:plain

これでユーザープール並びに Google 側の設定は終わりです。

Flutter アプリに Cognito を組み込む

1. Cognito ライブラリ

今回は、Cognito を組み込むためのライブラリは公式の amplify_flutter を使います。

非公式でも amazon_cognito_identity_dart_2 は使いやすくて、筆者も公式がリリースされるまではこちらを利用していました。

このライブラリを使って、Cognito のソーシャルサインインの方法をわかりやすく解説されている記事はこちらになります。とても参考になりました。

qiita.com

公式はまだリリースから間もないため、バグやその解決方法の解説が少ない恐れがあります。

ですので、個人的には、現時点では商用のアプリケーションで使う場合は非公式の方を使って、公式が安定したら置き換えるような対応が良いと思っています。

2. パッケージインストール

Cognito を使う場合は amplify_auth_cognito も必要なので、プロジェクトの pubspec.yaml に記載して flutter pub get します。

# pubspec.yaml

amplify_flutter: 0.2.4
amplify_auth_cognito: 0.2.4

3. Flutter アプリの設定

Flutter アプリに Cognito の設定ファイルを追加する必要があるので、以下を参考に amplifyconfiguration.dart というファイルを作成して lib の直下に配置してください。

// ./lib/amplifyconfiguration.dart

const amplifyconfig = ''' {
  "UserAgent": "aws-amplify-cli/2.0",
  "Version": "1.0",
  "auth": {
      "plugins": {
          "awsCognitoAuthPlugin": {
              "IdentityManager": {
                  "Default": {}
              },
              "CredentialsProvider": {
                  "CognitoIdentity": {
                      "Default": {
       // 今回は使わないので任意
                          "PoolId": "[COGNITO_IDENTITY_POOL_ID]",
       // 今回は使わないので任意
                          "Region": "[COGNITO_IDENTITY_POOL_REGION]"
                      }
                  }
              },
              "CognitoUserPool": {
                  "Default": {
      // ユーザープールの ID、全般設定から確認ができる
                      "PoolId": "[COGNITO_USER_POOL_ID]",
         // アプリクライアントの ID、全般設定 >アプリクライアント から確認ができる
                      "AppClientId": "[COGNITO_USER_POOL_APP_CLIENT_ID]",
                       // ユーザープールの ID のアンダースコアまでの値
                      "Region": "[COGNITO_USER_POOL_REGION]"
                  }
              },
              "Auth": {
                  "Default": {
                      "OAuth": {
                            // ユーザープールのドメイン名、アプリの統合 > ドメイン名から確認ができる
                            "WebDomain": "[COGNITO_DOMAIN_PREFIX].auth.[COGNITO_USER_POOL_REGION].amazoncognito.com",
                            // 上記と同じアプリクライアントの ID
                            "AppClientId": "[COGNITO_USER_POOL_APP_CLIENT_ID]",
                            // アプリの統合 > アプリクライアントの設定 のコールバック URL
                            "SignInRedirectURI": "[SIGN_IN_REDIRECT_URI]",
                            // アプリの統合 > アプリクライアントの設定 のサインアウト URL
                            "SignOutRedirectURI": "[SIGN_OUT_REDIRECT_URI]",
                            // アプリの統合 > アプリクライアントの設定 の許可されている OAuth スコープを配列として入れる、例)["phone", "email", ...] 
                            "Scopes": ["[OAUTH_SCOPES]"]
                       },
                      // 全般設定 > アプリクライアント の認証フローの設定で有効にしてある中からアプリで使うフローを入れる、例)"USER_PASSWORD_AUTH"
                      "authenticationFlowType": "[AUTHENTICATION_FLOW_TYPE]"
                  }
              }
          }
      }
  }
}''';

github.com

4. iOS の設定

公式にあるように iOS 側の設定も必要です。

https://docs.amplify.aws/lib/auth/social_signin_web_ui/q/platform/flutter/#android-platform-setup

サインインの際に開いた Web UI からアプリにリダイレクトするための設定です。

プロジェクトの ./ios/Runner/Info.plist に以下を追加してください。amplify-flutter の部分にはこちらで設定したサインイン URL から '://' を取り除いた値を入れてください。

...
<dict>
    ...
    <!-- amplify flutter setting -->
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>amplify-flutter</string>
            </array>
        </dict>
    </array>
    ...
</dict>
...

github.com

5. Android の設定

公式にあるように Android 側の設定も必要です。

https://docs.amplify.aws/lib/auth/social_signin_web_ui/q/platform/flutter/#android-platform-setup

こちらもサインインの際に開いた Web UI からアプリにリダイレクトするための設定です。

プロジェクトの ./android/app/src/main/AndroidManifest.xml に以下を追加してください。こちらも amplify-flutter の部分にはこちらで設定したサインイン URL から '://' を取り除いた値を入れてください。

...
   <queries>
       <intent>
           <action android:name=
               "android.support.customtabs.action.CustomTabsService" />
       </intent>
   </queries>
   <application
    ...
        <activity android:name=
           "com.amplifyframework.auth.cognito.activities.HostedUIRedirectActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="amplify-flutter" />
            </intent-filter>
        </activity>
    </application>
...

github.com

6. Flutter アプリの実装

かなり簡易的に、サインイン済みならマイページを、未サインインならサインインページを開くための処理を mian.dart に書きました。

特に難しい点はないと思いますが、_sign メソッド内で呼び出している Amplify.Auth.signInWithWebUI がソーシャルサインインをするための API になります。

// ./lib/main.dart

import 'package:flutter/material.dart';

import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify.dart';

import 'package:amplify_flutter_social_signin/pages/loading_page.dart';
import 'package:amplify_flutter_social_signin/pages/mypage.dart';
import 'package:amplify_flutter_social_signin/pages/sign_in_page.dart';

import 'amplifyconfiguration.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isInitialized = false;
  AuthUser? _user;

  @override
  initState() {
    super.initState();
    _initAmplifyFlutter();
  }

  void _initAmplifyFlutter() async {
    AmplifyAuthCognito auth = AmplifyAuthCognito();
    Amplify.addPlugins([auth]);

    try {
      await Amplify.configure(amplifyconfig);

      final user = await Amplify.Auth.getCurrentUser();

      setState(() {
        _user = user;
      });
    } on AmplifyAlreadyConfiguredException catch (e) {
      print(e.message);
    } on SignedOutException catch (e) {
      print(e.message);
    }

    setState(() {
      _isInitialized = true;
    });
  }

  Widget _display() {
    if (!_isInitialized) {
      return const LoadingPage();
    } else {
      return _user != null
          ? MyPage(
              username: _user!.username,
              userId: _user!.userId,
              signOut: _signOut,
            )
          : SignIn(
              signIn: _signIn,
            );
    }
  }

  Future<void> _signOut() async {
    try {
      await Amplify.Auth.signOut();
      setState(() {
        _user = null;
      });
    } on AuthException catch (e) {
      print(e.message);
    }
  }

  Future<void> _signIn() async {
    try {
      final res = await Amplify.Auth.signInWithWebUI(provider: AuthProvider.google);
      if (res.isSignedIn) {
        try {
          final user = await Amplify.Auth.getCurrentUser();

          setState(() {
            _user = user;
          });
        } on SignedOutException catch (e) {
          print(e.message);
        }
      }
    } on AuthException catch (e) {
      print(e.message);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Amplify Flutter App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: _display(),
    );
  }
}

7. 動作確認

一応動作の確認をします。こちらがサインインページになります。

f:id:HiroTanifuji:20211003125924p:plain

サインインボタンによって Amplify.Auth.signInWithWebUI が呼び出されると、アクセスに関するダイアログが表示されます。

そこで Continue が押されると、Web UI で Google のログインページが表示されます。

f:id:HiroTanifuji:20211003130035p:plain

Google のログインが完了すると、アプリにリダイレクトされ、マイページが表示されます。

f:id:HiroTanifuji:20211003130048p:plain

さいごに

Flutter アプリに Amazon Cognito を使って、Google のソーシャルサインインを実装する手順について解説しました。

ご不明点やご指摘などがございましたら、コメントに書いてください。

読んでいただきありがとうございました。