Cognitoを使ってみたい初心者、案件で検討している人向け（大体1.5年くらい使っての所感@2020/5時点

## 概要

2020/05/16 時点の話。

感想
安易に採用すると後で大変なことに。。。って思うけどやらないとノウハウも溜まらないよねっていう。

## 案件の条件

### 推奨する案件の条件

* 別システムの認証まで含めた統合的な認証システムを作りたい場合
    * 正し、この場合、認証システムを作るっていう別案件を立てるのを激しく推奨。。
* 別プロバイダでの認証（Googleとか）も許容したい場合
* MFAで電話番号、メールアドレスを使いたい場合
* とにかく認証機能を作りたくない場合

### 推奨しない案件の条件

* 認証の要件が不確定 or ぐるぐる変わりそうな場合
    * Cognitoにできないことは地力でカバーする必要が出てきて、Cognitoが足かせになる
* 本当にごく簡単なユーザ登録、認証さえできれば良い場合
    * 例えば、ID/PWで認証できて、登録時にメール飛べばそれだけで良いみたいな軽いヤツ  
      後からCognitoの仕様に振り回されるくらいなら自分達作った方が楽
    * まー、簡単だからとりあえず使ってみてノウハウを作るっていう考え方もある

## 始める前に理解しておくべき技術

* IDトークンとは何か
* IDトークンの検証方法

"ホストされた UI"機能を使う場合は↓の各フロー（Implicit Flow 等）の流れ
（使わないなら知らなくても良い（CognitoのAPI叩くだけで完結するので、まー、知ってた方が良いけど。。））

* Auth2.0
* OpenIDConnect

※"ホストされた UI" を使う場合、"アプリクライアントの設定" を突き付けられるので嫌でも覚えることになるとは思う

## 所感（メモに近い感じになったけど。。

### ■ ユーザープール／IDプールとは何か

* "ユーザプール" は、ユーザの管理、認証。
* "IDプール" は、一時クレデンシャル情報の発行。 別プロバイダ用のIDトークン生成。   
  ※IDプールはクレデンシャル情報（AWSの一時的なアクセスキー）を発行許可するユーザプール（厳密にいうとIDプロバイダ）を指定する形式

なので、認証情報（IDトークン）が欲しいだけなら、ユーザプールだけでもいいし、
クレデンシャル情報が欲しいだけなら、ユーザプールはいらない（独自認証して、クレデンシャルだけ発行できる。

#### おまけ

##### IDトークンって何

Cognitoがこの人認証しましたよっていう情報。

##### アクセストークンって何

Cognitoのユーザプール用のAPIを叩くときに渡すトークン。

### ■ クレデンシャル・IDトークンだけ欲しい場合、ユーザ管理・認証にユーザプールを使わないで自前のユーザ管理・認証でも出来る

独自認証については [こちら](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/authentication-flow.html) の "開発者が認証した ID の認証フロー" を参照

要は自前で認証処理を作り、

* 自前の認証を実行する
* 認証OKだったら、 GetOpenIdTokenForDeveloperIdentity を呼ぶ  
  ※レスポンスの Token は独自認証したID用のIDトークン
* GetOpenIdTokenForDeveloperIdentity のレスポンス値を使って、GetCredentialsForIdentity を呼ぶ
* 一時クレデンシャルが貰える

という流れ。

### ■ 一度設定したIDは変更できない

変更できないので、メールアドレス等、時間経過で変更が考えられるモノをIDに使おうとする場合、注意。

### ■ パスワードポリシーはCognito縛りがある

Cognitoがサポートしているポリシー以外は結局自分たちでカバーする必要がある。

例えば、過去に使用されたパスワードを使ってはいけない、細かいパスワード文字列の条件等、Cognito直接ではなく、専用のAPIを用意し、API内でパスワードのチェック、CognitoのAPIコールをすることになる

### ■ トークンには必ず期限がある

IDトークン、アクセストークンは更新トークンが生きていれば何度でも好きなタイミングで再生成できる。
が、更新トークンは期限の延長はできない（1～3650日まで）。

ので、ID、アクセストークンが再発行できなくなるタイミングはいつか必ず来る。
（更新トークンを再発行するために、ID/PWの再認証をする必要がある）



### ■ IDトークンの有効期限は1時間固定である

IDトークンの有効期限は1時間固定。IDトークンの有効期限を使って1時間より短い何かをしようとする時は注意。
（例えばブラウザのセッションタイムアウトの判定に使おうとする等）

※
ただ、独自認証時、GetOpenIdTokenForDeveloperIdentity でトークン発行する場合は、トークンの期限を1時間以外に指定できる模様。。

### ■ アクセストークンを使えば認証したユーザへやりたい放題できる

クライアントにIDトークン、アクセストークンが残る場合（Amplifyとか）、アクセストークンを使ってCognitoのAPIを直接叩けば、認証した本人に対してAPIで出来ることは全てできる。（はず、、

* 例えば、APIを直接叩いて自分自身を削除、パスワード変更することができる。  
  （これは実際にやって確認済み
* クライアントアプリの設定である程度制御が効く模様（どこまで抑止できるようになるのは未調査）  
※アプリクライアント > "属性の読み込みおよび書き込みアクセス権限を設定する" > "書き込み可能な属性" の nickname にだけチェック入れると、addressに対しては更新を防げる。

あくまで、"認証した自分自身に対して"に限定される話ではあるが、良しとするかどうかは判断が必要。

#### この対策としては "Authorization Code Flow"（ホストされたUI）を使うことになる

これが、嫌な場合、"ホストされた UI"機能を使い、Authorization Code Flow にする必要がある。
（authorization codeを発行するためのAPIはないため。（AWSに問い合わせてないって言われた

この場合、クライアントが直接、CognitoのAPIと直接通信することは無くなる。
発行される authorization code （リダイレクトのパラメータに含まれる）をAPI等に送信し、Cognitoとの通信を代行する形。

アプリクライアントの "アプリケーションのシークレット" を設定し、それが必要になるようにするのを推奨
（APIが内部でシークレットの値を保持し、Cognitoと通信する時に使用する
（"client id" と "authorization code" は外から見える情報で、それを使ってトークンが取得できるため、開発側しか知りえない情報（APIの内部値）として "アプリケーションのシークレット" を使って制限をかける

※ただ、こちらの方法の場合、Amplify等との相性は悪くなると思うので、アクセストークンを使って直接APIを叩くけることをどこまで許容するかのジャッジが必要。
（Amplifyは使えなくなる可能性高い（試したことない）。Amplifyがサインイン時に取得、LocalStorageへ設定しているパラメータが無くなるので。なるべくAmplifyが作り出す状況を自力で再現した場合にどこまでできるかは謎。

##### "ホストされた UI"を使う時の参考

[Amazon Cognito ユーザープール の Auth API リファレンス](https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html)

#### もしくは、Cognitoへの認証はAPIを用意し、APIの中でCognitoの認証をやって、レスポンスとしてIDトークンだけ返すとかそういうのかな。。

って思います。
※ただ、こちらの方法もAmplifyと相性悪い（"Authorization Code Flow"と同じ理由）。

### ■ 二要素認証はできる

とりあえず実績があるのは、ID/PW/2要素のコードを画面で同時に入力して、Amplifyで認証、認証前トリガーを使って、2要素のコードを事前にチェックするっていう形式。

詳細は [こっち参照](https://qiita.com/k_hoso/items/bc3897aa366348919b9a) ※2段階にはなってないので注意

ちなみに、AWSコンソールに2段階でログインする場合は、ID/PWの画面（リクエスト送らない）→MFA画面（ID/PWも含めてリクエスト送る）になっている。（仮に間違ったPWを入力していたとしても、MFAの入力する画面へ行く）

カスタムチャレンジを自分で追加する方がオシャレなのかもしれない。。

"ホストされた UI"の方でも出来るかどうかは不明。。