AWS Cognito ✗ Laravelを使った会員機能構築の話

# はじめに #
ここ3ヶ月程、某WEBサイトに会員機能＋決済機能を追加するお仕事をしてました。
基本的に設計〜リリースまで自分主担当で進めるにあたり、なんか便利な外部サービスないかなと目をつけたのがAWS Coginitoでした。

# 導入対象のシステム構成 #
インフラはAWSを使っており、サーバーサイド＆フロントはphp×Laravel構成です。

# 導入の背景 #
リリースまで３ヶ月、運用を楽にしたい、個人情報周りを自社で持つようなことをしたくないというのを軸にいい感じのサービスないかなーと探しておりました。
AWSでいい感じのマネージドサービスあるんじゃないかと思って見つけたCognito。さっとドキュメント読んだ感じ、ソーシャルIDプロバイダーを介したサインインや多要素認証（MFA）といった機能もあり導入を決めました。

# 検証・設計時に苦労したこと #
cognitoで検索すると結構な数の記事が出てきたのですが、どれもAWS Amplifyを使ったモダンな利用方法ばかり。。。自分のようにサーバーサイドで使う例があまり見当たらず、また公式ドキュメントもamplifyを使った例しか見つけれず中々時間がかかりましたが、
aws-sdk-phpの[CognitoIdentityProviderClient](https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.CognitoIdentityProvider.CognitoIdentityProviderClient.html)を利用すればできそうなところまでなんとかあたりをつけられました。

# Cognito側の設定 #
まずはマネジメントコンソールからUserPoolsを作成します。
で出来上がったのがこちら。
<img width="1434" alt="スクリーンショット 2019-11-30 14.19.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307594/0a20f1cb-71c4-10b1-0bb0-d1e2be1c7f05.png">
<img width="1233" alt="スクリーンショット 2019-11-30 14.26.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307594/f9d9d4c7-07d6-1806-8226-dd86c2546883.png">

[CognitoIdentityProviderClient](https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.CognitoIdentityProvider.CognitoIdentityProviderClient.html)のAPI呼び出しにおいて、プールID(UserPoolId)およびアプリクライアントID(ClientId)を指定しなければならないものもあるため確認しておきます。
また、トークンの有効期限を更新（日）の項目もAPI呼び出しに必要なTokenの有効期限を設定するもののため適切な値に適宜設定すると良いと思います。

# サーバーサイドからのCognitoAPI呼び出し #
私が主に使用したのは認証・取得・更新のAPIになります。
それぞれ下記のような形で呼び出せます。

```php:sample.php
$email = $request->input('email');
$password = $request->input('password');

$client = new CognitoIdentityProviderClient([
            'profile' => 'default',
            'region'  => 'ap-northeast-1', // 東京リージョンの場合
            'version' => '2016-04-18'
        ]);
$result = $client->initiateAuth([
    'AuthFlow' => 'USER_PASSWORD_AUTH',
    'ClientId' => 'アプリクライアントID',
    'AuthParameters' => ['USERNAME' => $email, 'PASSWORD' => $password],
]);
```
レスポンスはこんな感じです。

```
[
    'AuthenticationResult' => [
        'AccessToken' => '<string>',
        'ExpiresIn' => <integer>,
        'IdToken' => '<string>',
        'NewDeviceMetadata' => [
            'DeviceGroupKey' => '<string>',
            'DeviceKey' => '<string>',
        ],
        'RefreshToken' => '<string>',
        'TokenType' => '<string>',
    ],
    'ChallengeName' => 'SMS_MFA|SOFTWARE_TOKEN_MFA|SELECT_MFA_TYPE|MFA_SETUP|PASSWORD_VERIFIER|CUSTOM_CHALLENGE|DEVICE_SRP_AUTH|DEVICE_PASSWORD_VERIFIER|ADMIN_NO_SRP_AUTH|NEW_PASSWORD_REQUIRED',
    'ChallengeParameters' => ['<string>', ...],
    'Session' => '<string>',
]
```
この中で重要なのがAccessTokenとRefreshTokenになります。
AccessTokenは認証後にユーザー情報の取得や更新等他のAPIを呼び出す際にKeyとして利用します。ただし、有効期限は発行から１ｈです。
次にRefreshTokenですが、AccessTokenおよびIdToken（今回は利用していない）の再発行に利用するためのTokenです。
こちらの有効期限は前述の **トークンの有効期限を更新（日）**で設定した期間になります。
ちなみに取得したtoken達はLaravelのsessionで保管をしています。

AccessTokenを使ってユーザー情報を取得するのはこんな感じになります。

```php:getuser.php
$accesstoken = session('access_token');
$result = $client->getUser([
    'AccessToken' => $accesstoken,
]);
```

# まとめ #
自身が調査しているときは情報が中々出てこなくて多少時間がかかりましたが、この記事を書いてる時点でととても詳しく書かれているサーバーサイド✕Cognitoの記事もいくつか見つけられました。
システム構成上しかたなくでしたが、スクラッチで作るのであればやはりamplifyの方がもっと便利で簡単にできるかもしれません。
ある程度出来上がってしまっているシステムに対してCognitoを導入する際の参考に少しでもなれれば幸いです。