FlutterでAmazon CognitoのHosted UIを利用してみた

2022.05.14

こんにちは、CX事業本部IoT事業部の高橋雄大です。

CognitoでAzure AD(Azure Active Directory)を利用する際、組み込みUIではなくCognitoのHosted UI(ホストされたウェブUI)を使う必要があるため、FlutterでAmplifyのライブラリを使用してHosted UIを実装する方法について調べてみました。

Amplifyのライブラリを使用した組み込みUIの実装方法については次の記事で紹介しています。

本記事のゴール

Flutterで開発したiOSアプリからCognitoのHosted UIを利用します。

Flutter Cognito Hosted UI

環境情報

項目 内容
OS macOS Monterey 12.3 (21E230)
Visual Studio Code 1.66.0
Xcode 13.3 (13E113)
Flutter 2.10.4
Dart 2.16.2

Cognitoのアプリ設定

Cognitoドメインを作成します。必要があればカスタムドメインを設定します。

Cognitoドメイン設定

許可されているコールバックURLと許可されているサインアウトURLにアプリケーションのURLを登録します。URLは他のアプリケーションと重複しないようにする必要があります。

CognitoホストされたUIを編集

iOSのプロパティ設定

iOSのプロパティ設定にアプリのURLを追加します。Xcodeで設定してもOKです。

ios/Runner/info.plist

	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>myapp</string>
			</array>
		</dict>
	</array>

ログイン機能を実装

Amplifyのパッケージをインストールします。

flutter pub add amplify_auth_cognito
flutter pub add amplify_flutter

Amplifyの設定ファイルamplifyconfiguration.dartを作成します。amplify-cliを利用すれば自動で設定ファイルを生成することも可能ですが、Amplifyのセットアップが必要になるので、今回は手動でAmplifyの設定ファイルを作成します。

Amplifyの設定ファイルには、ユーザープールIDとアプリケーションクライアントIDを入力します。また、最初に作成ドメインと設定したコールバックURLも入力します。

lib/amplifyconfiguration.dart

const amplifyconfig = '''{
  "auth": {
    "plugins": {
      "awsCognitoAuthPlugin": {
        "CognitoUserPool": {
          "Default": {
            "PoolId": "[COGNITO USER POOL ID]",
            "AppClientId": "[COGNITO USER POOL APP CLIENT ID]",
            "Region": "[REGION]"
          }
        },
        "Auth": {
          "Default": {
            "OAuth": {
              "WebDomain": "[COGNITO WEB DOMAIN]",
              "AppClientId": "[COGNITO USER POOL APP CLIENT ID]",
              "SignInRedirectURI": "myapp://",
              "SignOutRedirectURI": "myapp://",
              "Scopes": [
                "email",
                "openid",
                "profile"
              ]
            }
          }
        }
      }
    }
  }
}''';

main.dartでログイン機能を実装します。

lib/main.dart

import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter/material.dart';

import 'amplifyconfiguration.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool signedIn = false;

  Future<void> _configure() async {
    try {
      await Amplify.addPlugin(AmplifyAuthCognito());
      await Amplify.configure(amplifyconfig);

      try {
        await Amplify.Auth.getCurrentUser();

        setState(() {
          signedIn = true;
        });
      } on AuthException {
        setState(() {
          signedIn = false;
        });
      }

      print('Successfully configured');
    } on Exception catch (e) {
      print('Error configuring Amplify: $e');
    }
  }

  @override
  void initState() {
    super.initState();
    _configure();
  }

  Future<void> signIn() async {
    try {
      await Amplify.Auth.signInWithWebUI();

      setState(() {
        signedIn = true;
      });
    } on AuthException {
      setState(() {
        signedIn = false;
      });
    }
  }

  Future<void> signOut() async {
    try {
      await Amplify.Auth.signOut();

      setState(() {
        signedIn = false;
      });
    } on AuthException {
      setState(() {
        signedIn = true;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ホーム')),
        body: Center(
          child: Center(
              child: signedIn
                  ? ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        primary: Colors.red,
                      ),
                      child: const Text('ログアウト'),
                      onPressed: () {
                        signOut();
                      })
                  : ElevatedButton(
                      child: const Text('ログイン'),
                      onPressed: () {
                        signIn();
                      })),
        ),
      ),
    );
  }
}

ログイン前はログインボタン、ログイン後はログアウトボタンが表示されます。

Flutter Amplify Hosted UIログイン

デバッグコンソールに次のようなエラーが出力される場合にはios/Podfileでプラットフォームを設定する必要があります。AmplifyのパッケージはiOS11以上が要件になりますので、プラットフォームを指定する必要があります。エラー内容を見ると、プラットフォームが指定されていないので、iOS9が自動で選択されているようです。

o update
    [!] CocoaPods could not find compatible versions for pod "amplify_auth_cognito_ios":
Error output from CocoaPods:
↳
    [!] Automatically assigning platform `iOS` with version `9.0` on target `Runner` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
Error running pod install
Error launching application on iPhone 13 Pro Max.
Exited (sigterm)

ios/Podfile2行目のコメントアウトを解除してプラットフォームにiOS11を指定します。

ios/Podfile

# Uncomment this line to define a global platform for your project
platform :ios, '11.0'

Podのキャッシュを更新します。

pod repo update

動作確認

CognitoのHosted UIでログインできることを確認します。

Flutter Cognito Hosted UI

まとめ

CognitoのHosted UIは日本語に対応していないため、普段はあまり使うことがありませんが、Azure ADなどの外部IdPと連携する際には利用することになります。Hosted UIも組み込みUIと同様にAmplifyのライブラリで簡単に実装することができました。

参考資料