3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Japan AWS Jr. ChampionsAdvent Calendar 2024

Day 24

Amazon Verified Permissionsで外部IDプロバイダ(Keycloak)を使用してみる

Last updated at Posted at 2024-12-23

はじめに

本記事では、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は正常に動作しました。

本記事でやりたいこと

image.png

本記事では、上記構成を構築し、次のフローを実現します。

①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メソッドを作成します。

タイトルなし.png

この段階では認可の設定を入れていないので、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のトップページから、「ポリシーストアを作成」を選択します。
image.png

2.起動オプションから、「API Gateway と ID プロバイダーによるセットアップ - 新品です」(新品です...?)を選択します。
image.png

3.リソースとアクションをインポートするために、上記で作成したAPI Gatewayをインポートします。
image.png

4.IDソースの設定をします。
image.png

以下の表に、IDソースの各設定値をまとめました。

設定項目 設定値 説明
OpenID コネクトプロバイダの種類 外部 OIDC プロバイダー Keycloakを使用するため。この設定を選択します。
OIDC プロバイダーの詳細 https://<Your-Keycloak-Issuer-URL> IDプロバイダ(Keycloak)のIssuerの値を入力します。URLはhttps://で始まる必要があります。
トークンの種類 アクセストークン アクセストークンを選択します。これはAPIリクエスト時に用いるトークンの種類です。

※その他の設定値については、今回はデフォルトの値を使用します。

5.ポリシーストアでは、グループに関連付けられるアクションを指定できます。今回はグループによる承認を行いませんが、グループの設定が必要であるため、許可されるアクションにチェックを入れます。
image.png

6.アプリ統合をデプロイします。今回は試験的に作成しているため、APIの認証を開始するタイミングを「今」に設定します。これにより、CloudFormationがLambda AuthorizerやAPI Gatewayの設定を自動的に行ってくれます。
image.png

image.png

デプロイされるとこのような表示になります。
image.png

ポリシーの設定

構築完了したので、認可のためのポリシーを定義していきます。

IDプロバイダのトークンをスキーマにマッピングする

デフォルトのスキーマのままでは、トークンのクレームをポリシー定義で利用することができません。そのため、公式ドキュメントを参考にして、IDプロバイダのトークンをスキーマにマッピングします。

1.左タブのスキーマを選択し、「スキーマを編集」をクリックします。
image.png

デフォルトで作成されているスキーマ定義を修正します。
これにより、ポリシー定義で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.左タブからポリシーを選択し、「ポリシーの作成」→「静的ポリシーの作成」を選択します。

image.png

2.以下の通りポリシーの範囲を設定します。今回はアクションの範囲のみ設定しました。
image.png

3.JSON形式でポリシーを編集します。

image.png

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から発行された以下のペイロードを持つアクセストークンを使用してリクエストを送信してみます。
重要なのは、scopereadが含まれている点です。

{
  "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から発行された以下のペイロードを持つアクセストークンを使用してリクエストを送信してみます。
重要なのは、scopereadが含まれていない点です。

{
  "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の設定を自動で構築してくれる点が特にありがたかったです。
一方で、トークンのスキーマへのマッピングまわりは、設定方法が分からず戸惑うことが多かったです。
今後はより複雑なポリシー設定も調査してみたいです!

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?