FlutterとAWSで始めるサービス開発 (5)AWS Cognitoでログイン

# はじめに
前回の「[(4)AWS Cognitoへの新規アカウント登録](https://qiita.com/makotomi/items/d2e399d16c0931ad3228)」では、Cognitoユーザープールに新規のアカウントを追加しました。今回はそのアカウントを利用してログインを行いたいと思います。

# 前回からの変更点
前回のコードからの変更点を順に説明します。

## ログインセッション

ログインセッションを保存するためのグローバル変数を追加しました。

```dart
CognitoUserSession session;
```

## MaterialAppのroutesにログイン結果画面用の定義を追加

__'/TopPage'__という名前で、ログイン後に遷移する画面の定義を追加しました。

```dart
      routes: <String, WidgetBuilder>{
        '/': (_) => new MyHomePage(),
        '/TopPage': (_) => new TopPage(),
        '/RegisterUser': (_) => new RegisterUserPage(),
        '/ConfirmRegistration': (_) => new ConfirmRegistration(null),
      },
```

## ログイン画面の変更

ログインを実行するために、メールアドレス、パスワードを入力するためのテキストフィールド、ログインボタンを追加しました。ログインボタンを押下したら、**_signIn()**メソッドが実行されるように実装しています。


``` dart
class MyHomePage extends StatelessWidget {
  final _mailAddressController = TextEditingController();
  final _passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ログイン'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: 'test@examle.com',
                  labelText: 'メールアドレス',
                ),
                controller: _mailAddressController,
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: 'password',
                  labelText: 'パスワード',
                ),
                obscureText: true,
                controller: _passwordController,
              ),
            ),
            Container(
              alignment: Alignment.centerRight,
              padding: const EdgeInsets.all(8.0),
              child: RaisedButton(
                child: Text('ログイン'),
                color: Colors.indigo,
                shape: StadiumBorder(),
                textColor: Colors.white,
                onPressed: () => _signIn(context),
              ),
            ),
            Divider(color: Colors.black),
            RaisedButton(
              child: Text('新しいアカウントの作成'),
              color: Colors.indigo,
              textColor: Colors.white,
              shape: StadiumBorder(),
              onPressed: () => Navigator.of(context).pushNamed('/RegisterUser'),
            ),
          ],
        ),
      ),
    );
  }

  void _signIn(BuildContext context) async {
    var cognitoUser = new CognitoUser(_mailAddressController.text, userPool);
    var authDetails = new AuthenticationDetails(
        username: _mailAddressController.text,
        password: _passwordController.text);
    try {
      session = await cognitoUser.authenticateUser(authDetails);
      Navigator.of(context).pushReplacementNamed('/TopPage');
    } catch (e) {
      await showDialog<int>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return AlertDialog(
            title: Text('エラー'),
            content: Text(e.message),
            actions: <Widget>[
              FlatButton(
                child: Text('OK'),
                onPressed: () => Navigator.of(context).pop(1),
              ),
            ],
          );
        },
      );
    }
  }
}
```

実装した画面は以下になります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273926/7e8ec213-8769-0b30-2d8e-4f9aca44c36c.png" width="50%" />


## ログイン結果画面の追加

以下のようにログイン結果画面を追加しました。ログインセッションのアクセストークンのJWTを画面に表示するのみです。

```dart
class TopPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('トップページ'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('ログイン成功'),
            Divider(color: Colors.black),
            Text(session.getAccessToken().getJwtToken()),
          ],
        ),
      ),
    );
  }
}
```

実装した画面は以下になります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273926/d9b9ac8d-e285-1e52-a248-345e9c606e8f.png" width="50%" />

# ログイン

実際にログインする際の処理を改めて説明します。__class MyHomePage__の**_signIn()**メソッドに処理があります。ログイン画面で入力されたメールアドレスとパスワードを利用します。まず、__CognitoUserクラス__をインスタンス化します。引数にはユーザー名であるメールアドレスとユーザープールのインスタンスになります。次に、ログインに必要な詳細情報を__AuthenticationDetailsクラス__をインスタンス化して作ります。メールアドレスとパスワードをセットします。最後に、ログインに必要な詳細情報を引数に__CognitoUserクラス__の__authenticateUserメソッド__を実行することでログイン処理が行われサーバーのセッションを取得できます。

# 画面遷移

ログインに成功したら、``Navigator.of(context).pushReplacementNamed('/TopPage')``にて画面遷移をします。__pushReplacementNamedメソッド__は今存在する画面スタックをすべて置き換えて、引数で与えられた'/TopPage'の画面に置き換えます。これにより下記で示すような画面遷移をし、ログイン結果画面がスタックに積まれた唯一の画面として表示されま。

![画面遷移 Step2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273926/5540e550-c435-da02-fe37-f928408f233c.png)

# まとめ
ユーザープールに作成したアカウントでのログイン処理を確認できました。次回「[(6)AWS Cognito 「パスワードを忘れた方はこちら」](https://qiita.com/makotomi/items/d1a2fadb6bc9be39f21e)」とし、ユーザーがログインパスワードを忘れてしまった場合の処理を作っていきたいと思います。