背景・目的
Amazon Cognito ユーザープールをオーソライザーとして使用して REST API へのアクセスを制御するのチュートリアルを試してみます。
まとめ
今回は、下記の構成で試しました。
概要
Amazon Cognito ユーザープールをオーソライザーとして使用して REST API へのアクセスを制御するを基に、整理します。
IAM ロールとポリシーまたは Lambda オーソライザー (以前のカスタムオーソライザー) の代わりに、Amazon Cognito ユーザープールを使用して、Amazon API Gateway の API にアクセスできるユーザーを制御します。
- Amazon Cognitoユーザープールを使用して、API GatewayのAPIにアクセスできるユーザを制御する
API で Amazon Cognito ユーザープールを使用するには、COGNITO_USER_POOLS タイプのオーソライザーを作成してから、そのオーソライザーを使用する API メソッドを構成する必要があります。API がデプロイされた後、クライアントはまずユーザーをユーザープールに署名し、ユーザーの ID またはアクセストークンを取得してから、トークンの 1 つ (通常はリクエストの Authorization ヘッダーに設定されている) で API メソッドを呼び出す必要があります。API 呼び出しは、必要なトークンが提供され、提供されたトークンが有効な場合にのみ成功します。そうでない場合、クライアントは認証された認証情報を持たないため呼び出しを許可されません。
- Cognitoユーザプールを使用するには、COGNITO_USER_POOLタイプのオーソライザを作成
- そのオーソライザーを使用するAPIメソッドを構成する
- クライアントは、ユーザをユーザプールに署名し、ユーザID、アクセストークンを取得しトークンの1つでAPIメソッドを呼び出す
- API呼び出しは、必要なトークンが提供される
- 提供されたトークンが有効な場合にのみ成功する
- そうではない場合、クライアントは認証された認証情報を持たないため呼び出しは許可されない
ID トークンは、サインインされたユーザーの ID リクエストに基づいて API 呼び出しを承認するために使用されます。アクセストークンは、指定されたアクセス保護されたリソースのカスタムスコープに基づいて API 呼び出しを承認するために使用されます。
- IDトークンは、サインインされたユーザのIDリクエストに基づき、API呼び出しを承認するために使用される
- アクセストークンは、指定されたアクセス保護されたリソースのカスタムスコープに基づき、API呼び出しを承認するために使用する
ユーザープールでのトークンの使用
ユーザープールでのトークンの使用を基に整理します。
トークンを使用してユーザーを認証し、リソースへのアクセス権を付与します。トークンのクレームはユーザーに関する情報です。ID トークンには、ユーザー名、苗字、E メールアドレスなど、身元に関するクレームが含まれています。アクセストークンには、認証されたユーザーがサードパーティー API、Amazon Cognito ユーザーのセルフサービス API オペレーション、および UserInfo エンドポイント にアクセスできる scope のようなクレームが含まれています。アクセストークンと ID トークンの両方に、ユーザープール内でのユーザーのグループメンバーシップを示す cognito:groups クレームが含まれています。ユーザープールグループの詳細については、「ユーザープールにグループを追加する」を参照してください。
- トークンを使用してユーザを認証し、リソースへのアクセス権を付与する
- トークンのクレームは、ユーザに関する情報
- IDトークンには、ユーザ名等の身元に関するクレームが含まれる
- アクセストークンには、
- 認証されたユーザがサードパーティAPI
- UserInfoエンドポイントにアクセスできるスコープ
- アクセストークンとIDトークンの両方には、ユーザプール内でのユーザグループメンバーシップを示すcongnito:groupsクレームが含まれる
Amazon Cognito には、新しいトークンの取得や既存のトークンの取り消しに使用できる更新トークンもあります。トークンを更新して、新しい ID とアクセストークンを取得します。トークンを取り消して、更新トークンによって許可されているユーザーアクセスを取り消します。
- Cognitoには、新しいトークンの取得や既存のトークンの取り消しに使用できる更新トークンもある
- トークンを更新して、新しいIDとアクセストークンを取得する
- トークンを取り消して、更新トークンにより許可されているユーザアクセスを取り消す
Amazon Cognito は、Base 64 でエンコードされた文字列としてトークンを発行します 任意の Amazon Cognito ID またはアクセストークンを base64 からプレーンテキスト JSON にデコードできます。Amazon Cognito の更新トークンは暗号化されており、ユーザープールのユーザーと管理者に対して透過的ではなく、ユーザープールのみが読み取ることができます。
- Cognitoは、Base64でエンコードされた文字列としてトークンを発行する
- 任意のCognito IDまたはアクセストークンをbase64からプレーンテキストJSONにデコードできる
- Cognitoの更新トークンは暗号化されており、ユーザープールのユーザと管理者に対して透過的ではなく、ユーザプールのみが読み取れる
トークンを使用した認証
ユーザーがアプリケーションにサインインすると、Amazon Cognito がログイン情報を検証します。ログインが正常に行われると、Amazon Cognito がセッションを作成し、認証されたユーザーの ID トークン、アクセストークン、および更新トークンを返します。これらのトークンを使用して、ダウンストリームのリソースや Amazon API Gateway などの API へのアクセス権をユーザーに付与できます。または、これらのトークンを他の AWS のサービスにアクセスするための一時的な AWS 認証情報と交換することができます。
- ユーザがアプリケーションにサインインすると、Cognitoがログイン情報を検証する
- ログインが正常に行われると、Cognitoがセッションを作成し、認証されたユーザのIDトークン、アクセストークン、更新トークンを返す
- トークンを使用し、ダウンストリームのリソース、API GatewayなどのAPIへのアクセス権をユーザに付与できる
- これらのトークンを他のAWSサービスにアクセスするための一時的なAWS認証情報と交換できる
トークンの保存
アプリケーションでは、さまざまなサイズのトークンを保存する必要があります。トークンのサイズは、クレームの追加、エンコーディングアルゴリズムの変更、暗号化アルゴリズムの変更による理由(ただし、これらに限定されない)で変更される場合があります。ユーザープールでトークンの取り消しを有効にした場合も、Amazon Cognito が JSON ウェブトークンにクレームを追加するので、そのトークンのサイズは増加します。アクセストークンと ID トークンに対し、新たなクレーム origin_jti および jti が追加されます。トークンの取り消しの詳細については、「トークンの取り消し」を参照してください。
- アプリケーションでは、様々なサイズのトークンを保存する必要がある
トークンをカスタマイズする
Amazon Cognito からアプリに渡すアクセストークンと ID トークンをカスタマイズできます。トークン生成前の Lambda トリガーでは、トークンクレームを追加、変更、および抑制できます。トークン生成前トリガーは、Amazon Cognito からデフォルトのクレームセットを送信する先の Lambda 関数です。クレームには、OAuth 2.0 スコープ、ユーザープールグループのメンバーシップ、ユーザー属性などが含まれます。この関数は、必要に応じてランタイムに変更を加え、更新したトークンクレームを Amazon Cognito に返すことができます。
バージョン 2 のイベントによるアクセストークンのカスタマイズには追加料金がかかります。詳細については、「Amazon Cognito の料金」を参照してください。
- Cognitoからアプリに渡すアクセストークンとIDトークンをカスタマイズできる
- トークン生成前のLambdaトリガーでは、トークンクレームを追加、変更、抑制できる
- トリガー生成前トリガーは、Cognitoからのデフォルトのクレームセットを送信する先のLambda関数
- クレームには、OAuth2.0スコープ、ユーザグループのメンバーシップ。ユーザ属性などが含まれる
- 必要に応じてランタイムに変更を加えて、更新したトークンクレームをCognitoに返す事ができる
実践
クラスメソッド様のCognitoで認証されたユーザーだけがAPI Gatewayを呼び出せるオーソライザーを使ってみたを基に試してみます。(ありがとうございます!)
1. ユーザプールの作成
REST API 用の Amazon Cognito ユーザープールを作成する
-
AWSにサインインします
-
Cognitoに移動し、「ユーザプールを作成」をクリックします
-
Cognitoユーザプールのサインインオプションに「ユーザ名」を選択し、「次へ」をクリックします
-
多要素認証では、MFAなしを選択します
-
ユーザアカウントの復旧では、下記を指定し、「次へ」をクリックします
-
属性検証とユーザアカウントの確認では、下記を選択します
-
最初のアプリケーションクライアントでは、下記を指定します
-
その他は、デフォルトのまま「次へ」をクリックします
-
確認と作成画面で「ユーザプールを作成」をクリックします
2. ユーザを作成する
-
作成したユーザプールを選択します
-
下記を入力し、「ユーザを作成」をクリックします
サインイン
-
下記のコマンドで仮パスワードで、認証フローを開始します
aws cognito-idp initiate-auth \ --auth-flow USER_PASSWORD_AUTH \ --client-id <your_app_client_id> \ --auth-parameters USERNAME=<your_username>,PASSWORD=<temporary_password>
-
レスポンスが返されるので、Sessionをコピーします
$ aws cognito-idp initiate-auth \ --auth-flow USER_PASSWORD_AUTH \ --client-id <your_app_client_id> \ --auth-parameters USERNAME=<your_username>,PASSWORD=<temporary_password> { "ChallengeName": "NEW_PASSWORD_REQUIRED", "Session": "XXXXXX", "ChallengeParameters": { "USER_ID_FOR_SRP": "XXXXXX", "requiredAttributes": "[]", "userAttributes": "{\"email\":\"XXXXX\"}" } } $
-
上記で取得したSessionを貼り付けて、チャレンジを送ります
aws cognito-idp respond-to-auth-challenge \ --client-id <your_app_client_id> \ --challenge-name NEW_PASSWORD_REQUIRED \ --session <session> \ --challenge-responses USERNAME=<your_username>,NEW_PASSWORD=<new_password>
-
下記のレスポンスが返されます
$ aws cognito-idp respond-to-auth-challenge \ --client-id <your_app_client_id> \ --challenge-name NEW_PASSWORD_REQUIRED \ --session <session> \ --challenge-responses USERNAME=<your_username>,NEW_PASSWORD=<new_password> { "ChallengeName": "MFA_SETUP", "Session": "XXXXXXX", "ChallengeParameters": { "MFAS_CAN_SETUP": "[\"SOFTWARE_TOKEN_MFA\"]" } }
-
ユーザを選択します
-
アクションで「MFA設定の更新」をクリックします
3. API Gatewayの作成
-
API Gatewayに移動します
-
下記を入力し、「APIを作成」をクリックします
-
APIのデプロイをクリックします
アクセス確認
4. オーソライザの作成と設定
- 上記で作成したAPIを選択します
- ナビゲーションペインで「オーソライザー」をクリックします
- 「オーソライザーを作成」をクリックします
- 下記を指定し、「オーソライザーを作成」をクリックします
動作確認
-
CLIでIDトークンを取得してみます
$ aws cognito-idp initiate-auth \ --auth-flow USER_PASSWORD_AUTH \ --client-id <your_app_client_id> \ --auth-parameters USERNAME=<your_username>,PASSWORD=<temporary_password> { "ChallengeParameters": {}, "AuthenticationResult": { "AccessToken": "XXXX", "ExpiresIn": 3600, "TokenType": "Bearer", "RefreshToken": "XXXXX", "IdToken": "XXXX" } }
オーソライザーをAPI Gatewayのメソッドに設定する
- API GatewayリソースのGETをクリックします
- メソッドリクエストで編集をクリックします
- メソッドリクエストの設定の認可に、作成したオーソライザーを指定し、「保存」をクリックします
- 「APIをデプロイ」をクリックします
エンドポイントにアクセスし動作確認する
ヘッダーにIDトークンを指定するため、CLIで確認しています。
-
CLIでも同様です
$ curl -X GET https://XXXXXX.execute-api.us-east-2.amazonaws.com/dev {"message":"Unauthorized"} $
-
IDトークンを指定し、リクエストするとアクセスできました
$ curl -X GET https://XXXXXX.execute-api.us-east-2.amazonaws.com/dev \ > -H "Authorization: Bearer XXXXXXXX" <html> <head> <style> body { color: #333; font-family: Sans-serif; max-width: 800px; margin: auto; } </style> </head> <body> <h1>Welcome to your Pet Store API</h1> <p> You have successfully deployed your first API. You are seeing this HTML page because the <code>GET</code> method to the root resource of your API returns this content as a Mock integration. </p> <p> The Pet Store API contains the <code>/pets</code> and <code>/pets/{petId}</code> resources. By making a <a href="/dev/pets/" target="_blank"><code>GET</code> request</a> to <code>/pets</code> you can retrieve a list of Pets in your API. If you are looking for a specific pet, for example the pet with ID 1, you can make a <a href="/dev/pets/1" target="_blank"><code>GET</code> request</a> to <code>/pets/1</code>. </p> <p> You can use a REST client such as <a href="https://www.getpostman.com/" target="_blank">Postman</a> to test the <code>POST</code> methods in your API to create a new pet. Use the sample body below to send the <code>POST</code> request: </p> <pre> { "type" : "cat", "price" : 123.11 } </pre> </body> </html> $
考察
今回は、API GatewayのオーソライザにCognitoのオーソライザを指定してみました。
API Gatewayはデプロイするとグローバルに公開されるので、利用者を限定したいときに簡単に制限できるのは嬉しいですね。
参考