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を使うかどうか

AWS側で用意されたページに飛ばされるので使わないケースの方が多そう

AmplifyのUIコンポーネントを使うかどうか

Hosted UIと同じでカスタマイズ性に欠けるので理想的には使わないのが良さそうだが、UIの作成と認証処理をかなり省けそう(+安全)なので一旦使ってみる
特にIDプロバイダからのサインインは作り方がさっぱりなので助かる

トークンのバックエンドでの検証

JSON Web トークンの検証 - Amazon Cognito

Amplify.configureの引数について

もしかするとCLIで作成するのがマナーなのかも

https://docs.amplify.aws/lib/client-configuration/configuring-amplify-categories/q/platform/js/#scoped-configuration

user_idに相当する値

accessTokenのusernameがユニークな値なのでこれが使える
またidTokenのcognito:usernameも同じ値だと思われる

ユーザープール属性 - Amazon Cognito

お客様のアプリケーションでユーザー名が不要の場合は、ユーザーにユーザー名を指定するように求める必要はありません。アプリでユーザー用の一意のユーザー名をバックグラウンドで作成できます。  
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をコミットしないようにしても最終的にビルドに埋め込まれるため閲覧可能になってしまうようです。

stackoverflow.com

じゃあどうすんのよって話ですが、仮に外部からcognitoへ不正アクセスされても、サインアップ後のemailやSMS検証で弾けるから大丈夫とのこと。(検証してなかったらアウト)

stackoverflow.com

というわけで.envに書いてgit管理しても致命的な状況にはならなそうですが、念のためCICD中に埋め込むようにしときます。

後記

移行完了しました。

バックエンド側で実装したトークン認証処理をcognito用に置き換えましたが、このトークンを自前で生成できないのとパースするために外部通信が必要でテストに困りました。

同じpayloadを持つJWTを作ってパースできれば認証処理以外のテストは通せるかということで、結局移行前に実装した処理をテストで引き続き使っています。