Amazon API Gateway + JWTオーソライザーでAzure ADのIDトークンクレームを表示してみた

2021.09.15

いわさです。

ここ最近Azure ADのOIDC周りを少し触っています。

上記では自前で検証などしていたのですが、今回はAmazon API Gatewayとオーソライザーを使ったIDトークンの検証を行ってみたいと思います。
といっても、API GatewayにはJWTオーソライザーが提供されており、簡単に検証を行うことが出来ます。

JWTオーソライザーでは以下のようなJWTの基本的な検証機能を有しています。

  • デコード
  • 発行者のパブリックキーを使った署名の確認
  • クレームの検証(kid, iss, aud, exp, nbf, iat, scope)

API Gateway + Lambda

組み込み対象のAPIを作成します。
バックエンド部分は一旦初期作成状態のLambda関数(Node14)とします。

exports.handler = async (event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

API Gatewayと統合し、Postmanで呼び出してみます。

Lambdaのレスポンスを表示できていますね。

JWTオーソライザーを登録

では、オーソライザーを登録しましょう。
Lambdaオーソライザーでカスタム処理を作ることも出来ますが、JWT検証のみであればJWTオーソライザーを使うと車輪のなんとかをせずに済むってところなのでしょうか。

JWTオーソライザーの登録にあたって、以下を登録します。

  • 発行者URL
  • アプリケーションID(クライアントID)

ドキュメントのIssueからフォーマットを取得し、テナントIDを置き換えます。

https://login.microsoftonline.com/common/.well-known/openid-configuration

{
    "token_endpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
    "token_endpoint_auth_methods_supported": [
        "client_secret_post",
        "private_key_jwt",
        "client_secret_basic"
    ],
    "jwks_uri": "https://login.microsoftonline.com/common/discovery/v2.0/keys",
    "response_modes_supported": [
        "query",
        "fragment",
        "form_post"
    ],
    "subject_types_supported": [
        "pairwise"
    ],
    "id_token_signing_alg_values_supported": [
        "RS256"
    ],
    "response_types_supported": [
        "code",
        "id_token",
        "code id_token",
        "id_token token"
    ],
    "scopes_supported": [
        "openid",
        "profile",
        "email",
        "offline_access"
    ],
    "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0",
    "request_uri_parameter_supported": false,
    "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",
    "authorization_endpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
    "device_authorization_endpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode",
    "http_logout_supported": true,
    "frontchannel_logout_supported": true,
    "end_session_endpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/logout",
    "claims_supported": [
        "sub",
        "iss",
        "cloud_instance_name",
        "cloud_instance_host_name",
        "cloud_graph_host_name",
        "msgraph_host",
        "aud",
        "exp",
        "iat",
        "auth_time",
        "acr",
        "nonce",
        "preferred_username",
        "name",
        "tid",
        "ver",
        "at_hash",
        "c_hash",
        "email"
    ],
    "kerberos_endpoint": "https://login.microsoftonline.com/common/kerberos",
    "tenant_region_scope": null,
    "cloud_instance_name": "microsoftonline.com",
    "cloud_graph_host_name": "graph.windows.net",
    "msgraph_host": "graph.microsoft.com",
    "rbac_url": "https://pas.windows.net"
}

登録に必要なテナントIDとアプリケーションIDは以下より取得可能です。

テナントIDを専用のものに変更した発行者URLをオーソライザーへ登録します。

https://login.microsoftonline.com/9b9e2fc6-dc1a-4d7f-97ff-e86600ac5b48/v2.0

先程と同じようにアクセスしてみます。

認証エラーになりました!

IDトークンを渡す

では、IDトークンを使って作成したAPIへアクセスしてみましょう。

IDトークンの取得方法は以下の記事を参照してください。

JWT検証後、Lambdaバックエンドでクレームへアクセスすることが出来ます。(参考
nameを取得してみましょう。

API Gateway は JWT を検証した後、トークン内のクレームを API ルートの統合に渡します。JWT クレームには、Lambda 関数などのバックエンドリソースがアクセスできます。

exports.handler = async (event) => {
    const response = {
        statusCode: 200,
        body: JSON.stringify(event.requestContext.authorizer.jwt.claims.name),
    };
    return response;
};

AuthorizationヘッダーにベアラートークンとしてIDトークンをセットしてリクエストを送信します。

検証を通過し、クレーム情報の取得に成功しました!
なお、不正なトークン(クライアントIDやテナントが異なるIDトークン、有効期限が切れたトークンなど)は401 Unauthorizedエラーとなることも確認出来ます。

まとめ

Azure ADに登録したアプリ情報を使って、JWTオーソライザーを構築してIDトークンの検証を行うことが出来ました。
カスタムLambdaでデコードして検証して、など行わなくて良いので楽ですね。