FlutterとAmazon Cognitoでログイン機能を実装してみた

2022.04.07

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

FlutterとAmazon Cognitoでログイン機能を実装してみたいと思います。AWS AmplifyがFlutterのサポートを開始したことで、FlutterでAmplifyの各パッケージが使えるようになりました。今回は認証に関するAmplifyのパッケージを利用して、ログイン機能を実装していきます。

本記事のゴール

ログインとアカウント登録の機能を実装します。

FlutterでAmazon Cognitoのログイン

環境情報

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

Amazon Cognitoを準備

Amazon Cognitoユーザープールとアプリケーションクライアントを作成します。アプリケーションクライアントはパブリッククライアントでクライアントのシークレットは生成しません。

Cognitoユーザープール作成

Cognitoユーザープールを選択してユーザープールIDを取得します。また、アプリケーションクライアントを選択してアプリケーションクライアントIDを取得します。

Cognitoユーザープール

ログイン機能を実装

FlutterのプロジェクトにAmplifyのパッケージを追加します。

flutter pub add amplify_auth_cognito
flutter pub add amplify_authenticator
flutter pub add amplify_flutter

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

Amplifyの設定ファイルには、先程取得したユーザープールIDとアプリケーションクライアントIDを入力します。また、Amazon Cognitoユーザープールの設定内容も入力します。

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": {
            "authenticationFlowType": "USER_SRP_AUTH",
            "socialProviders": [],
            "usernameAttributes": [],
            "signupAttributes": [
              "email"
            ],
            "passwordProtectionSettings": {
              "passwordPolicyMinLength": 8,
              "passwordPolicyCharacters": []
            },
            "mfaConfiguration": "OFF",
            "mfaTypes": [
              "SMS"
            ],
            "verificationMechanisms": [
              "EMAIL"
            ]
          }
        }
      }
    }
  }
}''';

main.dartでログイン機能を実装します。ログイン画面やアカウント登録画面はAuthenticatorコンポーネントで自動生成されます。

lib/main.dart

import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_authenticator/amplify_authenticator.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> {
  @override
  void initState() {
    super.initState();
    _configureAmplify();
  }

  void _configureAmplify() async {
    try {
      await Amplify.addPlugin(AmplifyAuthCognito());
      await Amplify.configure(amplifyconfig);
      print('Successfully configured');
    } on Exception catch (e) {
      print('Error configuring Amplify: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Authenticator(
      child: MaterialApp(
        builder: Authenticator.builder(),
        home: Scaffold(
          appBar: AppBar(title: const Text('ホーム')),
          body: const Center(
            child: Text('ログイン成功!', style: TextStyle(fontSize: 32.0)),
          ),
        ),
      ),
    );
  }
}

デバッグを実行してプレビューするとログイン画面が表示されます。

Flutter Cognitoログイン機能

デバッグコンソールに次のようなエラーが出力される場合には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

ソーシャルログイン対応

amplifyconfiguration.dartsocialProvidersにソーシャルプロバイダーを追加します。実際にソーシャルログインを利用するには、Amazon Cognito側の設定も必要になります。

lib/amplifyconfiguration.dart

        "Auth": {
          "Default": {
            "authenticationFlowType": "USER_SRP_AUTH",
            "socialProviders": [
              "GOOGLE",
              "FACEBOOK",
              "AMAZON",
              "APPLE"
            ],
            "usernameAttributes": [],

追加したソーシャルプロバイダーのログインボタンが表示されました。

Flutter Cognitoソーシャルログイン

日本語化対応

既存のコンポーネントを上書きして文言を日本語に変更します。

lib/resolver.dart

import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:flutter/material.dart';

class CustomButtonResolver extends ButtonResolver {
  const CustomButtonResolver();

  @override
  String signIn(BuildContext context) {
    return 'ログイン';
  }

  @override
  String signUp(BuildContext context) {
    return 'アカウント登録';
  }

  @override
  String forgotPassword(BuildContext context) {
    return 'パスワードを忘れた場合';
  }

  @override
  String confirm(BuildContext context) {
    return '登録';
  }

  @override
  String sendCode(BuildContext context) {
    return '再送信';
  }

  @override
  String lostCode(BuildContext context) {
    return '検証コードが届かない場合';
  }

  @override
  String signInWith(BuildContext context, AuthProvider provider) {
    switch (provider) {
      case AuthProvider.apple:
        return 'Appleでログイン';
      case AuthProvider.amazon:
        return 'Amazonでログイン';
      case AuthProvider.facebook:
        return 'Facebookでログイン';
      case AuthProvider.google:
        return 'Googleでログイン';
    }
  }

  @override
  String backTo(BuildContext context, AuthenticatorStep previousStep) {
    switch (previousStep) {
      case AuthenticatorStep.signIn:
        return "ログイン画面へ戻る";
      default:
        return "戻る";
    }
  }
}

class CustomInputResolver extends InputResolver {
  const CustomInputResolver();

  @override
  String title(BuildContext context, InputField field) {
    switch (field) {
      case InputField.username:
        return 'ユーザー名';
      case InputField.email:
        return 'メールアドレス';
      case InputField.password:
        return 'パスワード';
      case InputField.passwordConfirmation:
        return 'パスワード(確認)';
      case InputField.verificationCode:
        return '検証コード';
      default:
        return super.title(context, field);
    }
  }

  @override
  String hint(BuildContext context, InputField field) {
    final fieldName = title(context, field);
    return '$fieldNameを入力してください';
  }

  @override
  String confirmHint(BuildContext context, InputField field) {
    final fieldName = title(context, field);
    final replaceFieldName = fieldName.replaceFirst('(確認)', '');
    return '$replaceFieldNameを再度入力してください';
  }
}

class CustomMessagesResolver extends MessageResolver {
  const CustomMessagesResolver();
}

class CustomTitleResolver extends TitleResolver {
  const CustomTitleResolver();

  @override
  String signIn(BuildContext context) {
    return 'ログイン';
  }

  @override
  String signUp(BuildContext context) {
    return 'アカウント登録';
  }

  @override
  String confirmSignUp(BuildContext context) {
    return '確認';
  }
}

const stringResolver = AuthStringResolver(
  buttons: CustomButtonResolver(),
  inputs: CustomInputResolver(),
  messages: CustomMessagesResolver(),
  titles: CustomTitleResolver(),
);

AuthenticatorコンポーネントでstringResolverを指定します。

lib/main.dart

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

import 'amplifyconfiguration.dart';
import 'resolver.dart';
  @override
  Widget build(BuildContext context) {
    return Authenticator(
      stringResolver: stringResolver,
      child: MaterialApp(
        builder: Authenticator.builder(),
        home: Scaffold(
          appBar: AppBar(title: const Text('ホーム')),
          body: const Center(
            child: Text('ログイン成功!', style: TextStyle(fontSize: 32.0)),
          ),
        ),
      ),
    );
  }
}

ログイン画面やアカウント登録画面を日本語で表示することができました。

Flutter Cognito日本語化

ダークテーマ

MaterialAppコンポーネントを編集してダークテーマを適応してみます。

lib/main.dart

  @override
  Widget build(BuildContext context) {
    return Authenticator(
      stringResolver: stringResolver,
      child: MaterialApp(
        builder: Authenticator.builder(),
        theme: ThemeData.dark(),
        home: Scaffold(
          appBar: AppBar(title: const Text('ホーム')),
          body: const Center(
            child: Text('ログイン成功!', style: TextStyle(fontSize: 32.0)),
          ),
        ),
      ),
    );
  }
}

ダークテーマのログイン画面を確認することができました。

Flutter Cognitoダークモード

動作確認

最後にアカウント登録をやってみます。入力したメールアドレス宛に検証コードが届くので、検証コードを入力してアカウント登録を完了します。

Flutter Cognitoアカウント登録

まとめ

AWS Amplifyのパッケージを利用することで、Flutterで簡単にログイン機能を実装することができました。Amplify UIのドキュメントを確認してみると、テーマの変更やUIのカスタマイズ方法についても記載されています。ブランディングを意識してログイン画面にアプリのロゴを表示したりするのも良いかもしれません。

参考資料