はじめに
本記事では、Amazon Verified Permissionsでのリクエストの認可に、外部のOIDC IDプロバイダから発行されたトークンを使用する方法を試してみます。
外部IDプロバイダの使用方法を説明した記事はあまり見かけないため、皆さんの参考になれば幸いです!
Amazon Verified Permissionsとは
Amazon Verified Permissionsは、カスタムアプリケーション向けにスケーラブルで詳細な権限管理と認可機能を提供するサービスです。このサービスを利用することで、開発者はビジネスロジックから認可を分離し、安全かつ効率的なアプリケーション開発が可能になります。
特徴として、アクセス許可を詳細に定義するためには、Cedarポリシー言語を使用します。Cedarはオープンソースで、認可ポリシーの記述および、そのポリシーに基づく認可決定に利用されます。
また、Amazon Cognito、Amazon API Gateway、AWS IAM Identity Centerなど、他のAWSサービスと連携が可能です。これにより、ユーザー認証とリソースアクセス管理を包括的に提供できます。
IDプロバイダとしてAmazon Cognitoを使用して認証し、Amazon Verified Permissionsで認可する構成が一般的なようですが、2024年8月より外部IDプロバイダにも対応しました。
外部IDプロバイダとしては、Okta、CyberArk、Transmit Securityなどに対応しているようです。ただし、標準的なOIDCのJWT形式に従ったトークンを発行するIDプロバイダであれば問題なく機能する可能性が高いです(保証はありません)。実際に本記事で触れるKeycloakは正常に動作しました。
本記事でやりたいこと
本記事では、上記構成を構築し、次のフローを実現します。
①Keycloakでユーザー認証を行います。
②Keycloakからアクセストークンが発行されます。発行されるトークンのペイロード部分は以下のとおりです。
{
"exp": 1734956205,
"iat": 1734955905,
"jti": "9bc0e395-49fb-4c97-8b44-15271bc2f97b",
"iss": "https://xxxxx/realms/myrealm",
"aud": "account",
"sub": "a3db1040-8e10-4163-8bc6-55b7523d3b8f",
"typ": "Bearer",
"azp": "myclient",
"sid": "5ed49497-2b7e-439e-98a2-60ed42f7ed91",
"acr": "1",
"allowed-origins": [
"/*"
],
"realm_access": {
"roles": [
"default-roles-myrealm",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid read"
}
③発行されたアクセストークンをAuthorizationヘッダに追加して、APIリクエストを送信します。
④アクセストークンを利用して、Lambda Authorizerへ認可のリクエストを送ります。
⑤Amazon Verified Permissionsにより、ポリシーに基づいた認可処理を実行します。
今回は、「アクセストークンのscopeクレームにreadが含まれている場合、APIへのアクセスを許可する」というポリシーを設定します。
⑥認可に成功した場合、APIリクエストが許可され、処理が続行されます。
構築方法
Keycloak (ALB + EC2)
こちらは本記事の主旨と関係ないため説明は省略します。
EC2上にKeycloakを構築し、ALB経由でアクセスできるようにしています。
IDプロバイダを構築する際は、以下の点に注意してください。
- Issuer URLは
https://
形式の必要があります - IDプロバイダのディスカバリーエンドポイント(Issuer URL +
.well-known/openid-configuration
)は、Amazon Verified Permissionsから検出できる必要があります
API Gateway + API(AWS Lambda)
こちらも詳細は説明しません。
基本的なAPI GatewayとAWS Lambdaの構成になります。
モックAPIとして、適当なレスポンスを返すAWS Lambdaを作成します。
API GatewayではそのAPIへシンプルな、/
パスのGETメソッドを作成します。
この段階では認可の設定を入れていないので、API Gatewayへリクエストを投げると、APIのレスポンスが返ってきます。
$ curl --request GET \
--url https://xxxx.execute-api.ap-northeast-1.amazonaws.com/sample/
{"statusCode": 200, "body": "Hello from Lambda!"}
Amazon Verified Permissions
1.Amazon Verified Permissionsのトップページから、「ポリシーストアを作成」を選択します。
2.起動オプションから、「API Gateway と ID プロバイダーによるセットアップ - 新品です」(新品です...?)を選択します。
3.リソースとアクションをインポートするために、上記で作成したAPI Gatewayをインポートします。
以下の表に、IDソースの各設定値をまとめました。
設定項目 | 設定値 | 説明 |
---|---|---|
OpenID コネクトプロバイダの種類 | 外部 OIDC プロバイダー | Keycloakを使用するため。この設定を選択します。 |
OIDC プロバイダーの詳細 | https://<Your-Keycloak-Issuer-URL> |
IDプロバイダ(Keycloak)のIssuerの値を入力します。URLはhttps:// で始まる必要があります。 |
トークンの種類 | アクセストークン | アクセストークンを選択します。これはAPIリクエスト時に用いるトークンの種類です。 |
※その他の設定値については、今回はデフォルトの値を使用します。
5.ポリシーストアでは、グループに関連付けられるアクションを指定できます。今回はグループによる承認を行いませんが、グループの設定が必要であるため、許可されるアクションにチェックを入れます。
6.アプリ統合をデプロイします。今回は試験的に作成しているため、APIの認証を開始するタイミングを「今」に設定します。これにより、CloudFormationがLambda AuthorizerやAPI Gatewayの設定を自動的に行ってくれます。
ポリシーの設定
構築完了したので、認可のためのポリシーを定義していきます。
IDプロバイダのトークンをスキーマにマッピングする
デフォルトのスキーマのままでは、トークンのクレームをポリシー定義で利用することができません。そのため、公式ドキュメントを参考にして、IDプロバイダのトークンをスキーマにマッピングします。
1.左タブのスキーマを選択し、「スキーマを編集」をクリックします。
デフォルトで作成されているスキーマ定義を修正します。
これにより、ポリシー定義でscope
クレームを呼び出せるようになりました。
{
"MyAPI": {
"entityTypes": {
"User": {
"shape": {
"attributes": {},
"type": "Record"
},
"memberOfTypes": [
"UserGroup"
]
},
"UserGroup": {
"shape": {
"attributes": {},
"type": "Record"
}
},
"Application": {
"shape": {
"attributes": {},
"type": "Record"
}
}
},
"actions": {
"get /": {
"appliesTo": {
"context": {
- "type": "Record",
- "attributes": {}
+ "type": "ReusedContext"
},
"principalTypes": [
"User"
],
"resourceTypes": [
"Application"
]
}
}
- }
+ },
+ "commonTypes": {
+ "ReusedContext": {
+ "attributes": {
+ "token": {
+ "type": "Record",
+ "attributes": {
+ "scope": {
+ "type": "Set",
+ "element": {
+ "type": "String"
+ }
+ }
+ }
+ }
+ },
+ "type": "Record"
+ }
+ }
}
}
ポリシーの作成
いよいよポリシーを作成します。
以下の公式ドキュメントでは、IDプロバイダのアクセストークンのクレームを参照するポリシーの作成方法が示されています。
これを参考にしながら作成していきます。
1.左タブからポリシーを選択し、「ポリシーの作成」→「静的ポリシーの作成」を選択します。
2.以下の通りポリシーの範囲を設定します。今回はアクションの範囲のみ設定しました。
3.JSON形式でポリシーを編集します。
context.token.scope.contains("read")
は、アクセストークンのscope
クレームにread
が含まれていることを表しています。
- permit(principal, action in [MyAPI::Action::"get /"], resource);
+ permit (
+ principal,
+ action in [MyAPI::Action::"get /"],
+ resource
+ )
+ when
+ {
+ context.token.scope.contains("read")
+ };
これでポリシーの設定が完了です!!
動作確認
認可成功の場合
Keycloakから発行された以下のペイロードを持つアクセストークンを使用してリクエストを送信してみます。
重要なのは、scope
にread
が含まれている点です。
{
"exp": 1734971645,
"iat": 1734971345,
"jti": "f4149dd8-ac56-4e83-b327-a4a74883fca0",
"iss": "https://m5-takeda.org/realms/myrealm",
"aud": "account",
"sub": "a3db1040-8e10-4163-8bc6-55b7523d3b8f",
"typ": "Bearer",
"azp": "myclient",
"sid": "ffcaf936-5931-45ff-8bbb-0db17c4d2281",
"acr": "1",
"allowed-origins": [
"/*"
],
"realm_access": {
"roles": [
"default-roles-myrealm",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid read"
}
APIからのレスポンスは以下の通りです。
認可に成功し、APIから200レスポンスが返ってきました。
$ curl --request GET \
--url https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/sample/ \
--header 'Authorization: bearer <access-token>'
{"statusCode": 200, "body": "Hello from Lambda!"}
認可失敗の場合
次は、Keycloakから発行された以下のペイロードを持つアクセストークンを使用してリクエストを送信してみます。
重要なのは、scope
にread
が含まれていない点です。
{
"exp": 1734971861,
"iat": 1734971561,
"jti": "9da9ebd4-2790-418f-8d60-41378ae298cd",
"iss": "https://xxxxx/realms/myrealm",
"aud": "account",
"sub": "a3db1040-8e10-4163-8bc6-55b7523d3b8f",
"typ": "Bearer",
"azp": "myclient",
"sid": "9b85c6b8-d720-438f-bbfc-06cb3a9b06b9",
"acr": "1",
"allowed-origins": [
"/*"
],
"realm_access": {
"roles": [
"default-roles-myrealm",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid"
}
APIからのレスポンスは以下の通りです。
認可に失敗し、エラーメッセージが返ってきました。
$ curl --request GET \
--url https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/sample/ \
--header 'Authorization: bearer <access-token>'
{"Message": "User is not authorized to access this resource with an explicit deny"}
さいごに
本記事では、Amazon Verified Permissionsを利用した外部IDプロバイダとの連携により、認可フローを構築する方法を紹介しました。
初めてAmazon Verified Permissionsを触ってみた感想としては、CloudFormationがLambda AuthorizerやAPI Gatewayの設定を自動で構築してくれる点が特にありがたかったです。
一方で、トークンのスキーマへのマッピングまわりは、設定方法が分からず戸惑うことが多かったです。
今後はより複雑なポリシー設定も調査してみたいです!