Flutter3 と Amplify UI Components で MFA(SMS)が有効な認証つきアプリを作ってみました

2022.09.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX事業本部デリバリー部の平内(SIN)です。

Flutterでの利用も提供されている、Amplify UI Componentsを使用すると、Cognitoと組み合わせて、ユーザーログインが簡単に準備できます。 なお、Amplify UI Componentsは、MFAにも対応しているため、Cognito側で設定を有効にするだけで、特に実装を追加する必要はありません。

今回は、Amplify UI Componentsを使用して、MFA対応の認証付きアプリを作成してみました。 ログイン時、及び、パスワードを忘れた場合(パスワード変更時)に、MFA(SMSに送られた検証コードの入力)が必要になります。

最初に、作成したアプリが動作している様子です。

ログイン時は、ユーザー名及び、パスワードに加えて、SMSに送られた検証コードの入力が必要です。 なお、一度ログインが完了するとログアウトしない限り、リフレッシュトークンの有効な期間(今回は30日と設定)は、認証なしで開けます。

2 動作環境

今回使用したバージョンは、以下の通りです。

% amplify --v
9.2.1
% flutter --version
Flutter 3.3.2 • channel stable • https://github.com/flutter/flutter.git
Framework • revision e3c29ec00c (8 days ago) • 2022-09-14 08:46:55 -0500
Engine • revision a4ff2c53d8
Tools • Dart 2.18.1 • DevTools 2.15.0

3 Flutterでのアプリ作成

最初に、Flutterアプリを作成し、必要なライブラリを追加します。

% flutter create sample_app
% cd sample_app
% flutter pub add amplify_flutter
% flutter pub add amplify_auth_cognito
% flutter pub add amplify_authenticator

4 Amplify(Auth)設定

続いて、Amplifyを初期化します。

% amplify init

Authの追加は、マニュアルで行います。

% amplify add auth
 Do you want to use the default authentication and security configuration?
  Default configuration
  Default configuration with Social Provider (Federation)
❯ Manual configuration
  I want to learn more.

使用するのは、サインアップとサインインのみです。

 Select the authentication/authorization services that you want to use:
  User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analytics, and more)
❯ User Sign-Up & Sign-In only (Best used with a cloud API only)
  I want to learn more.

プロジェクト名及び、ユーザープール名は、デフォルトのままとしました。

Provide a friendly name for your resource that will be used to label this category in the project:
 sampleapp519f64ce519f64ce
Provide a name for your user pool: 
 sampleapp519f64ce_userpool_519f64ce

ログインは、ユーザー名を使用することにします。

 How do you want users to be able to sign in? (Use arrow keys)
❯ Username
  Email
  Phone Number
  Email or Phone Number
  I want to learn more.

UserPoolのグループ、管理用APIは、いづれもONとしました。

 Do you want to add User Pool Groups?
❯ No
 Do you want to add an admin queries API?
❯ No

ここが、MFAの設定ですが、認証時必須とし、SMSを用いるよう設定します。

 Multifactor authentication (MFA) user login options:
  OFF
❯ ON (Required for all logins, can not be enabled later)
  OPTIONAL (Individual users can use MFA)
  I want to learn more.
 For user login, select the MFA types: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◉ SMS Text Message
 ◯ Time-Based One-Time Password (TOTP)

SMSのメッセージの設定や認証コードを送るEメールの設定です。 デフォルトのまま。

 Specify an SMS authentication message: (Your authentication code is {####})

パスワードを忘れた場合は、SMSを使用するようにします。

 Email based user registration/forgot password:
  Enabled (Requires per-user email entry at registration)
❯ Disabled (Uses SMS/TOTP as an alternative)

この時のSMSメッセージもデフォルトのままとしています。

Please specify an SMS verification message: (Your verification code is {####})

デフォルトのパスワードポリシーは、そのまま利用します。

 Do you want to override the default password policy for this User Pool? (y/N)

サインアップ時に必要な情報は、すべてのチェックを外しておきます(電話番号は自動的に必須となります)

 What attributes are required for signing up?
 ◯ Zone Info (This attribute is not supported by Facebook, Google, Login With Amazon, Signinwithapple.)
 ◯ Address (This attribute is not supported by Facebook, Google, Login With Amazon, Signinwithapple.)
 ◯ Birthdate (This attribute is not supported by Login With Amazon, Signinwithapple.)
❯◯ Email
 ◯ Family Name (This attribute is not supported by Login With Amazon.)
 ◯ Middle Name (This attribute is not supported by Google, Login With Amazon, Signinwithapple.)
 ◯ Gender (This attribute is not supported by Login With Amazon, Signinwithapple.)

トークンの有効期限は、デフォルトの30日です。

 Specify the app's refresh token expiration period (in days): 30
 Do you want to specify the user attributes this app can read and write? No

機能の追加、ソーシャルログイン、Lmabda関数のトリガーは特に使い予定がないため選択無しで行っていきます。

 Do you want to enable any of the following capabilities? (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯ ◯ Add Google reCaptcha Challenge
 ◯ Email Verification Link with Redirect
 ◯ Add User to Group
 ◯ Email Domain Filtering (denylist)
 ◯ Email Domain Filtering (allowlist)
 ◯ Custom Auth Challenge Flow (basic scaffolding - not for production)
 ◯ Override ID Token Claims

OAuthフローは、未使用です。

 Do you want to use an OAuth flow?
  Yes
❯ No
  I want to learn more.
? Do you want to configure Lambda Triggers for Cognito? (Y/n) N

マニュアルによる設定が完了したらpushします。

% amplify push

5 SMS送信

AmplifyのAuth設定で、SMSを有効にすると、下記のような警告が表示されます。

⚠️ You have enabled SMS based auth workflow. Verify your SNS account mode in the SNS console: https://console.aws.amazon.com/sns/v3/home#/mobile/text-messaging
If your account is in "Sandbox" mode, you can only send SMS messages to verified recipient phone numbers.

AWSアカウントのSMS送信は、当初、サンドボックスモードとなっているため、認証に使用する電話番号は、検証済みとする必要があります。

SMSサンドボックスを終了し、本稼働にすいれば、電話番号の検証は必要なくなります。

また、アカウントの使用制限は、デフォルトで1USD/月となっています。

日本宛に送信する場合のSMS料金は1通あたり$0.07451であるため、1月あたり、13 通程度でリミットとなってしまいます。 これ以上の送信をする場合は、上限緩和申請、及び、ここでの設定値を変更する必要があります。

https://aws.amazon.com/jp/sns/sms-pricing/?nc1=h_ls

上限緩和申請する場合のサービスクォータの名称は、SMS Message Spending in USDです。

6 Flutterの実装

実装したのコードは、以下の通りです。

認証周りについては、Amplify UI Componentsに全てお任せとなっています。

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 'package:sample_app/amplifyconfiguration.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 _amplify = Amplify;

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

  Future<void> _configureAmplify() async {
    try {
      await _amplify.addPlugins([
        AmplifyAuthCognito(),
      ]);
      await _amplify.configure(amplifyconfig);
    } catch (e) {
      debugPrint(e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Authenticator(
      child: MaterialApp(
        debugShowCheckedModeBanner: true,
        theme: ThemeData(
          primarySwatch: Colors.red,
        ),
        builder: Authenticator.builder(),
        home: const HomePage(),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  final title = 'Sample';
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      drawer: AppDrawer(
        title: title,
      ),
      body: const Center(
        child: Text(
          "Hello World.",
        ),
      ),
    );
  }
}

class AppDrawer extends StatelessWidget {
  const AppDrawer({super.key, required this.title});

  final String title;

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: SingleChildScrollView(
        child: Column(
          children: [
            AppBar(
              title: Text(title),
              automaticallyImplyLeading: false,
            ),
            ListTile(
              leading: const Icon(Icons.exit_to_app),
              title: const Text('Logout'),
              onTap: () {
                Amplify.Auth.signOut();
              },
            ),
          ],
        ),
      ),
    );
  }
}

7 最後に

今回は、Amplify UI Componentsを使用して、MFA対応の認証付きアプリを作成してみました。 認証周りは、自前で実装なると、準備するページを沢山あって、結構手間がかかります。提供されるコンポーネントで表現できる範囲で治ると、助かるのですが・・・