Cognitoへの移行
ログイン周りの処理を自前で実装してある程度理解したのでcognitoに移行させたい
ユーザーIDはcognito側から払い出されたものを自前のユーザーテーブルで二重管理するイメージになるんだろうか
IDプロバイダ側(Googleなど)の設定
Adding social identity providers to a user pool - Amazon Cognito
react向けチュートリアル
Amazon Cognito と AWS Amplify を使用して React アプリケーションユーザーを認証する - AWS 規範的ガイダンス
以下に実装しながら気づいた点などメモします。
- Hosted UIを使うかどうか
- AmplifyのUIコンポーネントを使うかどうか
- トークンのバックエンドでの検証
- Amplify.configureの引数について
- user_idに相当する値
- どのトークンをバックエンドの認証に使うのか
- トークンのリフレッシュ処理について
- googleソーシャルログインに審査が要るのかどうか
- terraformでaws_cognito_identity_providerを管理しようとすると差分が出る
- cognito関連のuser_pool_idなどをどこに保管するか??
- 後記
Hosted UIを使うかどうか
AWS側で用意されたページに飛ばされるので使わないケースの方が多そう
AmplifyのUIコンポーネントを使うかどうか
Hosted UIと同じでカスタマイズ性に欠けるので理想的には使わないのが良さそうだが、UIの作成と認証処理をかなり省けそう(+安全)なので一旦使ってみる
特にIDプロバイダからのサインインは作り方がさっぱりなので助かる
トークンのバックエンドでの検証
JSON Web トークンの検証 - Amazon Cognito
Amplify.configureの引数について
もしかするとCLIで作成するのがマナーなのかも
user_idに相当する値
accessTokenのusernameがユニークな値なのでこれが使える
またidTokenのcognito:usernameも同じ値だと思われる
お客様のアプリケーションでユーザー名が不要の場合は、ユーザーにユーザー名を指定するように求める必要はありません。アプリでユーザー用の一意のユーザー名をバックグラウンドで作成できます。 username はユーザープール内で一意である必要があります。username は再利用できますが、削除されて使用されなくなった場合のみです。
またcognitoのユーザープールを作成する際にサインインオプションとしてusernameとemailを必須にすると、usernameが固有な代わりに同じemailで複数のユーザーが登録できてしまい、想定する仕様(emailがunique)と異なるのでやめました。
ただcognitoでの(フォーム経由の)登録とソーシャルIdPからの登録が同じemailだった場合、それは重複してしまうようです。
どのトークンをバックエンドの認証に使うのか
cognitoから渡されるトークンはaccessToken, idToken, refreshTokenの3種類。
もちろんaccessTokenだろ!と思ってたのですが、それぞれの役割やpayloadを見てみると、IDトークンを使うのが正しそうです。
IDトークン:連携サービスの認証(例:API Gateway)と認証されたユーザー情報の参照 アクセストークン:Cognitoユーザープールのユーザー属性更新 更新トークン:新しいIDトークン、アクセストークンの取得
Cognitoのサインイン時に取得できる、IDトークン・アクセストークン・更新トークンを理解する | DevelopersIO
accessTokenはあくまでもcognitoに対するアクセスを許可するトークンという意味合いっぽいですね。
トークンのリフレッシュ処理について
アクセストークンの寿命を最短(5分)にして確認したところ、amplifyを使ってれば自動で更新してくれるようです。ありがとう。
googleソーシャルログインに審査が要るのかどうか
機密性の高いデータにアクセスする場合は必要だそうですが、機密性の高いデータとは何だろうか。
emailだけ取れれば十分なんですが・・・
AWS側のドキュメントによると最低限必要なscopeは3つ(email, profile, openid)ですが、OAuth同意画面を再確認したところ全て非機密のスコープにカテゴリされていたので審査は必要なさそうです。
terraformでaws_cognito_identity_providerを管理しようとすると差分が出る
authorize_url, oidc_issuerなどのドキュメントのexampleに載ってないパラメータがnullになる差分出ますが、applyして動作を確認したところ問題なさそう。
~ provider_details = { - "attributes_url" = "https://people.googleapis.com/v1/people/me?personFields=" -> null - "attributes_url_add_attributes" = "true" -> null ~ "authorize_scopes" = "openid email profile" -> "profile email openid" - "authorize_url" = "https://accounts.google.com/o/oauth2/v2/auth" -> null - "oidc_issuer" = "https://accounts.google.com" -> null - "token_request_method" = "POST" -> null - "token_url" = "https://www.googleapis.com/oauth2/v4/token" -> null
とはいえapplyする度に差分が出るので、ignore_changesで無視しようとのこと。 "aws_cognito_identity_provider" always have some changes on "provider_details" when "provider_type" is "Google" or "Facebook" · Issue #4831 · hashicorp/terraform-provider-aws · GitHub
cognito関連のuser_pool_idなどをどこに保管するか??
払い出されるid等はsecretとは書かれてないとはいえenvファイルに含めない方が良い感じがしますが、reactでは(webフロントエンド全般?).envをコミットしないようにしても最終的にビルドに埋め込まれるため閲覧可能になってしまうようです。
じゃあどうすんのよって話ですが、仮に外部からcognitoへ不正アクセスされても、サインアップ後のemailやSMS検証で弾けるから大丈夫とのこと。(検証してなかったらアウト)
というわけで.envに書いてgit管理しても致命的な状況にはならなそうですが、念のためCICD中に埋め込むようにしときます。
後記
移行完了しました。
バックエンド側で実装したトークン認証処理をcognito用に置き換えましたが、このトークンを自前で生成できないのとパースするために外部通信が必要でテストに困りました。
同じpayloadを持つJWTを作ってパースできれば認証処理以外のテストは通せるかということで、結局移行前に実装した処理をテストで引き続き使っています。