Closed32
CognitoのBlackBelt読んでみた
基本的に YouTube での内容を書き写しているだけです。
※2020年の時点の内容ですので、現在と異なる可能性があります。
Cognitoの概要
- ユーザープール
- 独自のユーザディレクトリ
- 外部IDプロバイダ
- 上記のログインに基づいてアプリへのアクセスに利用できるトークンを提供
- IDプール
- Cognitoユーザープール
- 外部IDプロバイダ
- 上記のログインに基づいてAWSにアクセスできるクレデンシャルを提供
ユーザープールの構成要素
- ユーザディレクトリ
- ユーザ
- 属性
- 外部IDプロバイダのユーザ
- 属性
- グループ
- ユーザ
- アプリクライアント
- 外部IDプロバイダ
- ポリシー設定
- MFA設定
- アドバンスドセキュリティ設定
- メッセージ設定
- Lambdaトリガー
- リソースサーバ
ユーザ
- 作成できる人
- 管理者
- ユーザ自身でサインアップを許可
- 外部IDプロバイダのユーザは初回サインイン時に自動作成
- Cognito固有情報
- Username
- Enabled
- UserStatus
- Groups
- PreferredMfaSetting
- UserMFASettingList
- roles
- Prefered_role etc
- 標準属性(OIDC)
- email_verified
- phone_number
- phone_number_verified
- preferred_username
- sub
- name
- updated_at etc
- カスタム属性
- ユーザについての任意の情報を保存可能
- 頻繁に参照・変更・検索する情報はDBに保存したほうが良い
- カスタム属性は検索する方法が限られており、一般的にはDBの方が便利
- DBとカスタム属性両方に保存すると両方の内容が矛盾しないように管理する必要が出てくる
- サインインや認可に必要なものはカスタム属性、そうでない情報はDBに
- サインインに使用したクライアントのデバイス情報(有効時にのみ記録)
- デバイスキー
- 名前
- 最後のIP
- SDK
- 最後の利用日時
ユーザがサインインに使用できる情報
- ユーザー名 + パスワード
- ユーザー名の代わりに、Eメール、電話番号、任意のユーザー名も利用できる
- これらの内容は、ユーザープール作成時に決める必要があり、後から変更できない
- 上記に加えて、MFAの設定も可能
- SMS、TOTP
- 全ユーザーにMFAを利用させる場合はユーザープール作成時に決める必要があり、後から変更できない
ユーザのステータス
- 様々なステータスがある
- UNCONFIRMED
- サインアップはしたが、管理者確認、ユーザー自身によるメールやSMSの確認、Lambdaトリガー(サインアップ前)の確認がされていない状況
- CONFIRMED
- 上記が確認された状況
- ユーザー移行Lambdaトリガーを利用した初回サインインの際もこのステータス
- DISABLED
- ユーザーが無効化
- RESET_REQUIRED
- ユーザーインポート時
- 管理者によりパスワードリセットされた状況
- パスワード設定により、CONFIRMEDに移る
- FORCE_CHANGE_PASSWORD
- 管理者によるユーザー作成後
- 管理者による一時パスワード設定後の状況
- るパスワード設定でCONFIRMEDに移る
- EXTERNAL_PROVIDER
- 外部IDプロバイダから初回サインイン後の状況
グループ
- ユーザは複数のグループに入れる
- ユーザの属性情報としてアプリから確認可能
- =アプリでそのグループの属性情報を利用できる
- IDプールとの連携で利用できるIAMロールやその優先順位の指定に利用可能
- 例、Admin グループは Admin ロールを渡すといった指定が可能
- Admin グループ(Admin ロール、優先順位:1)と ReadOnlyグループ(RedaOnly ロール、優先順位:2)にユーザが所属している場合、優先順位の高さを付けて渡すロールを決められる
- この場合は、Adminグループが優先されてAdmin ロールが渡される
アプリクライアント
- API呼び出し、Hosted UIの利用、OAuthによる認可などユーザープールにアクセスするアプリを登録
- アプリごとに利用する機能や権限を設定可能
- 登録されたアプリは、IAMクレデンシャルを利用せずにCognitoにアクセスできる
- アプリからのCognitoへのアクセスの際は、アプリクライアントIDで識別
- シークレットクライアントも発行できるが、サーバサイドからCognitoへアクセスする際に利用する
- プライベートクライアント
- フロントサイドからアクセスする際はシークレットクライアントは通常発行しない
- シークレットを安全に管理できないから
- パブリッククライアント
サインインの実装
- 利用できるAPIは2セット
- Cognito Identity Provider API
- サインインUIはアプリ側で作成
- サインインなど各種操作に応じたAPIが用意されている
- 呼出元によって異なるAPI
- クライアント側、サーバ側どちらから呼び出すかで使い分ける
- サーバーサイドから呼び出す Admin API
- 非SPAのWebでも利用でき、機能追加などの自由度が高い
- 認証情報:IAMロールクレデンシャル(IAMロールではなくIAMユーザーでもできる気がする)
- サインインのAdmin APIはアプリクライアントIDも必要
- 大抵のAPIは Admin がつくが、付いていないものもある
- クライアントから呼び出す 非 Admin API(サーバサイドからも可能)
- クライアントからも呼び出せるため、シンプルな実装にできる
- 認証情報:アプリクライアントID(+シークレット)
- サインイン後は、ユーザーのトークンで Cognito の API を呼び出す
- Cognito Auth API と Hosted UI
- サインインUIはユーザーではなくCognitoが用意したHosted UIで行う
- Hosted UI への入力の依頼と結果をもらうのがアプリの役割
サインインは認証フローを指定してAPIを呼び出す
- アプリクライアントは、許可されている認証フローを利用
- フローによっては、サインインに複数回の呼び出すが必要
- フロー
種類 | 説明 | API呼び出し回数 | Admin API からの呼び出し可否 | 非 Admin API からの呼び出し可否 |
---|---|---|---|---|
USER_SRP_AUTH | SRPプロトコルに基づいた パスワードを基にした チャレンジレスポンスで認証。 サーバ側でも元のパスワードは不明であり安全 |
MFAなし:2 MFAあり:3 |
o | o |
CUSTOM_AUTH | 認証時にLambda関数がトリガーされ、 関数の内容次第で追加の認証を行える |
1回以上 | o | o |
USER_PASSWORD_AUTH | SRPプロトコルを利用せず、 パスワード自体を送信して認証。クライアントからは USER_SRP_AUTHの利用を推奨 |
MFAなし:1 MFAあり:2 |
o | o |
ADMIN_USER_ PASSWORD_AUTH |
USER_PASSWORD_AUTHと似ているが、 サーバ用APIからのみ呼び出せる |
MFAなし:1 MFAあり:2 |
o | x |
REFRESH_TOKEN_AUTH | 更新トークンから新しいトークンをもらう | 1 | o | o |
- SRP
- セキュア・リモート・パスワード
- 生のパスワードを送らない
USER_SRP_AUTH - SRP プロトコルに基づいて認証 - MFA なし
- 生のパスワードは送信しない
- initiateAuth でこれから認証を行うフローを指定し、必要なパラメータを渡す
- Cognito は新しいチャレンジを指定しクライアントにレスポンス
- クライアントは指定されたチャレンジに応じたリクエストを RespondToAuthChallenge で返す
- 内容が正しければ Cognito はトークンを返す
USER_SRP_AUTH - SRP プロトコルに基づいて認証 - MFA あり
- 基本的に MFA なしと同一だが、MFA がある分チャレンジが1つ増える
- PASSWORD_VERIFIERにクリアした後も、SOFTWARE_TOKEN_MFAのチャレンジがある
CUSTOM_AUTH - カスタム認証
- Lambda 関数で、次のチャレンジの指定やチャレンジレスポンスの検証を行うことで、カスタマイズできる
- ユースケースの例
- Cognito とは別方式の MFA 認証を使う
- 月 1 回は秘密の質問を追加で確認する
- 人からのアクセスであることを検証するために CAPTCHA を確認する
- パスワードレス認証を行う
- 3 種類の Lambda 関数のトリガーで実現
- DefineAuthChallenge
- 認証チャレンジの定義
- CreateAuthChallenge
- 認証チャレンジの作成
- VerityAuthChallengeResponse
- 認証チャレンジレスポンスの確認
- DefineAuthChallenge
- SRP パスワード認証に加え、追加のワンタイムパスワード入力を求める場合
Cognito Autu API と Hosted UI を利用
- Cognito がマネージドサービスの一部として、サインアップ、サインイン、パスワード忘れの対応が行える Web の UI を提供
- MFA対応
- カスタム認証フローのような仕組みはなし
- ドメイン名、UIロゴ、CSSのカスタマイズは可能だが、日本語対応は不可
- 外部IDプロバイダーを使ったサインインには Auth API が必須
- OAuth を使ったサードパーティアプリへの認可には Auth API と Hosted UI が必須
認証は OAuth フローに沿って
- 認証は OAuth 2.0 で定義されているフローに沿って行う
- 認証後アプリには認可コードあるいはトークンをURLパラメータなどにつけてリダイレクトされる
- Authorization Code Grant 認可コード許可
- ユーザーがアプリにアクセス
- 認可エンドポイントを開き、画面遷移させる
- Hosted UI表示
- ユーザーがユーザー名・パスワード入力
- 認可エンドポイントに送信され、OKであれば 302 リダイレクトともに認可コードがユーザーにレスポンスされる
- 認可コードをトークンエンドポイントに送信し、トークンを入手する
- Implicit Grant 暗黙的許可
- 上記のフローで 302 リダイレクトともに認可コードではなくトークンがレスポンスされる
- セキュアでないので、Authorization Code Grantの利用が推奨される
エンドポイントへのアクセス
- エンドポイントへのリクエスト時のresponse_typeがcodeかtokenかにより、どちらのフローを利用するかが区別される
- Authorization Code Grant
- 認可エンドポイント
- GET
https://<指定したプレフィックス>.auth.{region}.amazoncognito.com/oauth2/authorize?response_type=code&client_id=<client_id>&redirect_uri=<callback url>&state=xxx&code_challenge_method=S256&code_challenge=xxx
- GET
- 認証後にリダイレクトされるURL
- GET
https://<callback url>?code=<認可コード>&state=xxx
- GET
- トークンエンドポイント
- POST
https://<指定したプレフィックス>.auth.{region}.amazoncognito.com/oauth2/token
- grant_type: authorization_code
- client_id: <client id>
- code: xxx
- redirect_url: <callback url>
- code_verifier: xxx
- POST
- 認可エンドポイント
- Implicit Grant
- 認可エンドポイント
- GET
https://<指定したプレフィックス>.auth.{region}.amazongocnito.com/oauth2/aurhorizer?response_type=token&client_id=<client id>&redirect_uri=<callback url>&state=xxx&code_challenge_method=S256&code_challenge=xxx
- GET
- 認証後にリダイレクトされるURL
- GET
https://<callback url>#id_token=xxx&access_token=xxx&expires_in=3600&token_type=Bearer&state=xxx
- GET
- 認可エンドポイント
- エンドポイントへのリクエスト時の redirect_url は、認証後にリダイレクトされるURLとなる
- アプリクライアントに設定したコールバックURLとも完全に一致する必要がある
- stateパラメータはCSRF対策
- 自ら呼び出してから、認可エンドポイントからリダイレクトされて戻ってきたのかを確認する
- 呼び出し時と同じstateの値であるか確認する
- 認可コードの横取り対策(PKCE(ピクシー), RFC7636)
- 認可コードを知った悪意のある第三者がシークレットを設定されていないアプリクライアントのフリをして、認可コードからトークンを取得するのを防ぐ
- 呼び出し側で決めた任意の値 code_verifier のハッシュ値を、認可エンドポイントへのリクエスト時に追加
- code_challenge_method 使用されるメソッド
- code_challenge ハッシュ値
- トークンエンドポイントへのリクエスト時に、code_verifier パラメータを追加する
- Cognito側で、認可エンドポイントにリクエストを行ったクライアントと同一であるか確認する
- identity_providerパラメータも利用できる
- IDプロバイダを明示指定することで、認可エンドポイントHosted UI を表示させずに直ぐに外部IDプロバイダのログインURLにリダイレクトさせれる
要件に応じたAPI選択
- 日本語対応を前提に考えると、以下の実装がおすすめ
- Hosted UI は日本語に対応していないため
実装には SDK や Library を利用する
- API を直接呼び出すより、SDK や Library を利用して呼び出すことがほとんどと思われる
- 表のタイトル説明
- ①: Identity Provider API を利用
- ②:Auth API と Hosted UI を利用
- ③:Identity ProviderAPI を利用する SDK 独自のサインイン UI
SDK | 動作環境 | 概要 | ① | ② | ③ |
---|---|---|---|---|---|
AWS SDK for JavaScript などの各種言語向け SDK | サーバやブラウザ | 低レベルなライブラリ | o | - | - |
Amazon Cognito Identity SDK for JavaScript | ブラウザ | ブラウザ向けのライブラリ | o 非Admin API |
- | - |
AWS Mobile SDK | iOS, Android | モバイル向けライブラリ | o 非Admin API |
△ 日本語化不可 |
△ 日本語化不可 |
AWS Amplify Library | ブラウザ、モバイル | 開発者が直感的に利用できる宣言型インターフェースライブラリ Mobile SDK や Cognito Identity SDK も内部で使用 |
o | △ 日本語化不可 |
△ 日本語化不可の場合も |
- Amazon Cognito Identity SDK for JavaScript
トークンの種類と用途
- サインイン時に発行されるトークンは3種類
- 含まれる情報が異なるため、用途も異なる
- サインインの方法によっては更新トークンは含まれない
- トークン
- IDトークン
- 有効期限1時間
- 利用することが多いと思われる
- 一部のAPIはアクセストークンの利用が必要だが(Identity Provider API のパスワード変更など)、ほとんどのAPIはIDトークンでも利用できる
- IDトークンの方がユーザーの情報が多い
- IDプールにアクセス
- アプリのAPIにアクセス
- APIGateway、AppSyncにアクセス
- アクセストークン
- 有効期限1時間
- アプリのAPIにアクセス
- APIGateway、AppSyncにアクセス
- Identity Provider API にユーザーとしてアクセス
- 更新トークン
- 有効期限1-3650日
- IDトークン・アクセストークンを更新
- IDトークン
サインイン後に発行されるトークン
- OAuth・OIDCと同様のJWT形式
- エンコード化される
- ヘッダ、ペイロード、署名の形式
- IDトークン
- カスタム属性含むユーザの属性が含まれる
- アクセストークン
- 一部のユーザ情報に加えスコープなどが含まれる
- 署名は以下の公開鍵で検証可能
- https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
入手したトークンをアプリのAPIへのアクセスに利用
- サーバでトークンを検証
- トークン内の署名は公開鍵により検証可能
- API Gateway 経由でアクセス
- APIGateway にCognito オーソライザーを設定
- API Gateway へのリクエスト時にトークンを追加し、認証に成功した場合にバックエンドのサービスにリクエストを行う
- AppSync経由でアクセス
- AppSyncにCognitoの認証を設定
- AppSync へのリクエスト時にトークンを追加し、認証に成功した場合にバックエンドのサービスにリクエストを行う
- ALB経由でアクセス
- ALBでCognito認証を設定
- ALBへのリクエスト時にトークンを追加し、認証に成功した場合にバックエンドのサービスにリクエストを行う
- Cognito Auth API & Hosted UI 前提
- 日本語対応不可
入手したトークンを AWS の API アクセスに利用
- IDトークンを、Cognito IDプールで、AWSの一時クレデンシャルを発行可能
- クレデンシャルが発行されるIAMロールを指定可能
- IDプールで指定した認証済みユーザ用のロール
- ユーザグループのグループに基づくロール
- ルールで指定されたロール
- クレデンシャルにはcognito-identity.amazonaws.com:subなどの変数が設定されており、S3などのアクセス条件に使用できる
サインアウト
- 共用端末などで、ユーザ本人以外が同じクライアントを使ってアプリを利用することを防ぐためにも、サインアウトを実装した方がいい
- トークンの利用方法により行う対処が異なる
- 共通
- トークンをアプリやSDKでクライアントから破棄
- トークンはアプリに渡しているからアプリで破棄する必要がある
- アクセストークン・IDトークン自体は最大1時間の有効期限内は利用可能
- Identity Provider API(Identity Provider APIをユーザとしての呼び出し(更新トークンの利用を含む)
- Identity Provider APIグローバルサインアウトで無効化
- Identity Provider API に関しては、アクセストークンも更新トークンも利用できなくなる
- Auth API
- Auth API ログアウトエンドポイントで無効化
- Hosted UIからの再サインインが必要になる
- OAuthによるサードパーティアプリへのAPIアクセス認可など、トークンの破棄が難しい場合はリスクに応じて追加の対処を検討する
- アプリを実装しているのはサードパーティなのでそこのトークンを破棄させるのは難しいと思われる
- (YouTubeの該当箇所の文字起こし)例えばOAuthによるサードパーティアプリにAPIアクセスを提供している場合、アプリケーションを実装されているのはサードパーティってことになりますので、そこのトークンを破棄させるのは結構難しいと思います
- トークンをアプリやSDKでクライアントから破棄
主なユーザ操作
- サインアップ
- ユーザ名、Email、電話番号を使用したサインアップ
- Email あるいは電話番号の確認
- アカウントを確認するためのEmailアドレスや電話番号の検証への対応
- サインイン
- ユーザ名、パスワード、MFAなどを使ってサインインしてトークンの入手
- パスワード忘れ対応
- 確認済みのメールアドレス、電話番号に確認コードを送り、確認できた場合にパスワードを変更
- AccountRecoverySetting優先順位をカスタマイズ可能
- パスワード変更
- 新しいパスワードを設定
- サインアウト
- サインアウトし、再度サインインしないとアプリを使えないようにする
- グローバルサインアウト
- ユーザに発行済みの全更新トークンを無効化
- IdP API では、ID・アクセストークンも無効化
操作 | IdP API Admin | IdP API 非Admin | Auth API & Hosted UI |
---|---|---|---|
サインアップ | (CreateUser) | SignUp | Hosted UI |
Email あるいは 電話番号の確認 |
Admin ConfirmSignup |
ConfirmSignUp | Hosted UI |
パスワード 忘れ対応 |
- | ForgotPassword | Hosted UI |
サインアウト | アプリやSDKで トークンを破棄 |
左同 | 左に加えて Logout Endpoint呼び出し |
グローバルサインアウト | AdminUser GlobalSignOut |
GlobalSignOut | - |
- パスワード変更のように、Autu API だけでは実現できない操作あり
- サーバサイドなどで、IdP API を利用してパスワード変更の API を呼びだす
主な管理者操作
- サーバサイドで実装し、ユーザから行えるようにすることも考えられる
操作 | 説明 | IdP API Admin |
---|---|---|
パスワードのリセット | 基本的にはパスワードの忘れ対応と同じ、ユーザにて確認操作が必要 | AdminReset UserPassword |
パスワードの強制変更 | パスワードを一時的な設定、あるいは恒久的な設定として変更 | AdminSet UserPassword |
ユーザを外部IDプロバイダとリンク | ユーザプール上の独自ユーザと外部IDプロバイダのユーザをリンクし同じユーザとして扱えるようにする | AdminLink ProviderForUser |
ユーザの無効化 | ユーザを無効化して使えないようにする | Admin DisableUser |
グループの作成 | グループを作成し、IAMロールや優先順位を振り分ける | CreateGroup |
ユーザの検索 | 標準属性について完全一致、前方部分一致するユーザをリストできる | ListUsers |
ユーザのインポート | CSVファイルを使用してユーザをインポートする | CreateUserImporJob |
サインイン入用できる外部IDプロバイダ
- Auth API を使うと、外部IDプロバイダのサインインに基づいて、Cognitoユーザープールにサインイン可能
- ユーザにとってはユーザ登録や新たなパスワードの記憶などの手間が省ける
- Login with Amazon
- Sign in with Apple
- SAML
- SP-Initiated 限定
- OpenID Connect(OIDC)
- LINE
- Yahoo etc
SP-Initiatedについてよく理解できていなかったので調べてみた
- SP-Initiatedとは?
- Service Provider がSAML認証を開始する方式
- Service Provider とは?
- SAML 認証でユーザーがログインするアプリを指す
- まとめると
- Cognito はアプリにアクセスして SAML 認証を開始することはサポートしている
- 反対に、IdP-Initiated、つまり IdP の SSO ポータルやダッシュボードなどから利用したいアプリ(SP、Cognito の SAML 認証においては Cognito に当たる)を選択して SAML 認証を開始することはサポートされていない
- なお、IdP のダッシュボードなどで Cognito ユーザープールの Authorize エンドポイントへのブックマークリンクを作成することで、IdP のダッシュボードから SP-Initeated で SAML 認証を開始することはできる模様(公式ドキュメントから)
セキュリティの強化
- 電話番号とEmailの検証
- MFAの利用
- SMSテキストメッセージ
- TOTPソフトウェアトークン
- クライアントデバイスの追跡
- アドバンスドセキュリティの機能
- 侵害された認証情報が使われていないか
- パスワード設定時や変更時に、そのユーザ名とパスワードのペアがインターネットに流出しているペアではないか
- アダプティブ認証
- リスクに応じてMFAの利用を求めたり、ブロックしたりする
- ユーザが利用している環境(デバイスやNWなど)から、判断する
- リスクが低ければMFAの入力を求めないなど
- 侵害された認証情報が使われていないか
メッセージのカスタマイズ
- メッセージを日本語にするなど、ユーザに送信されるメッセージをカスタマイズ可能
- 管理者が作成したユーザへの招待メッセージ
- SMS: 本文
- Email: サブジェクト、本文
- メールアドレス・電話番号の検証メッセージ
- SMS:本文
- Email:サブジェクト、本文、検証方法
- SMSを使ったMFAの認証メッセージ
- Lambda トリガーを使ったメッセージのカスタマイズを行うと、状況に応じて動的なメッセージを送信可能
- ユーザの言語情報をユーザの属性から知りうる場合、ユーザ属性の言語情報に応じたメッセージをLambdaトリガーで生成可能
Lambda トリガーを用いたカスタマイズ
カテゴリ | オペレーション | 説明 |
---|---|---|
カスタム認証フロー | 認証チャレンジの定義 | カスタム認証フロー内で次のチャレンジを決定 |
カスタム認証フロー | 認証チャレンジの作成 | カスタム認証フロー内でチャレンジを作成 |
カスタム認証フロー | 認証チャレンジレスポンスの確認 | カスタム認証フロー内でレスポンスが正しいか決定 |
サインイン | サインイン前 | サインインの許可・拒否をカスタムで決定 |
サインイン | サインイン後 | 分析のためにイベントログを記録できる |
サインイン | トークン生成前 | トークンのクレームを追加・更新・抑制 |
サインアップ | サインアップ前 | サインアップの許可・拒否をカスタムで決定 |
サインアップ | 確認後 | Welcomeメッセージなどのカスタマイズや分析用にイベントログを記録 |
サインアップ | ユーザー移行 | 既存のユーザディレクトリからユーザプールにユーザを移行 |
メッセージ | カスタムメッセージ | メッセージのカスタマイズ ユーザのロケールに合わせた言語で通知するなど |
Amazon Pinpoint - 分析
- ユーザの利用状況をPinpointに提供し、アプリやCognitoの利用状況を分析
- キャンペーンに利用できる情報を追加
- サインアップ
- サインイン
- 失敗した認証
- 1日あたりのアクティブユーザ DAU
- 1月あたりのアクティブユーザ MAU
CloudTrail - ログ記録
- 記録されるAPI
- Cognito Identity Provider API
- ユーザ情報やセキュリティ情報は記録されない
- 記録されない API
- Cognito Auth API や Hosted UI
- サインインユーザの情報をログ記録するにはLambda トリガー内で出力することで実現
CloudWatch - モニタリング
- 一部のAPIを対象に、ユーザープール、アプリクライアントごとに記録
- サインアップの成功・スロットリング
- サインインの成功・スロットリング
- トークン更新の成功・スロットリング
- フェデレーションの成功・スロットリング
クォータ
- API 呼び出しレートをクォータを超えた場合、スロットリングされて一時的なエラーとなる
- リソース数量などのクオータを超えると、失敗が返される
- 机上でのクォータ確認に加えて、実際に想定されている構成、アクセス量でのテストを行う
- テストは徐々にアクセス量を増やしながらテストを行う
- エラーやスロットリングが多く発生した場合は、それ以上の量でのテストはやめる
このスクラップは2024/01/14にクローズされました