Claude apps gatewayでES256署名やKeycloakのazpを通す設定
IdPがid_tokenをES256やEdDSAで署名する、Keycloakのブローカーやtoken exchangeでazpがclient_idと食い違う。そのとき触るのはoidcの2キーです。設定例と切り分けの順番をまとめます。
Claude apps gatewayのIdPがid_tokenをRS256以外で署名するなら、oidc.id_token_signed_response_algにそのアルゴリズムを書きます。Keycloakのブローカーやtoken exchangeでazpがclient_idと違うなら、oidc.additional_authorized_partiesにその値を足します。どちらも既定のままではサインインが通らない側のIdP向けの、2つの任意キーです。
設定ページの該当行はそれぞれ1文しかありません。ここでは、どんなIdPの構成で必要になるかと、書いたあとの確かめ方を順に見ます。全項目の表はgateway.yamlのリファレンスにあるので、ここは2キーとその周辺に絞ります。
どちらのキーも既定では何を期待しているのか
ゲートウェイはIdPが返したid_tokenを検証してから、メールとグループを読み取ります。この検証に関わる既定値が、次の2つです。
| キー | 既定の挙動 | 変えるとき |
|---|---|---|
id_token_signed_response_alg | 既定の挙動署名アルゴリズムにRS256を期待する | 変えるときIdPがES256・PS256・EdDSAで署名するとき |
additional_authorized_parties | 既定の挙動azpはclient_idと一致するものを受け付ける | 変えるときKeycloakのブローカー・token exchangeで別の値が入るとき |
2行目は設定ページの文面が「client_idに加えて受け付ける値」なので、既定はclient_idだけと読めます。どちらも必須ではなく、IdPが既定どおりなら書かなくて構いません。
id_token_signed_response_algはいつ必要になるのか
IdPがRS256以外でid_tokenに署名しているときです。設定ページは「ES256、PS256、EdDSAで署名するIdP向け」としか書いておらず、どのIdPがどのアルゴリズムを使うかは載せていません。自分のIdPの値は、ディスカバリー文書で確かめられます。
curl -s https://idp.example.com/.well-known/openid-configuration \
| grep -A6 id_token_signing_alg_values_supportedid_token_signing_alg_values_supportedは、OpenID Connectのディスカバリーで、IdPが使えるid_token署名方式を並べる項目です。ここに複数並ぶ場合、実際にそのクライアントのid_tokenへ使われる方式は、IdP側のクライアント設定で決まります。ディスカバリーの一覧だけでは1つに決められません。IdPの管理画面でクライアントごとの署名アルゴリズムを確認してから、gateway.yamlに書きます。
書き方は1行です。
oidc:
issuer: https://idp.example.com
client_id: claude-gateway
client_secret: ${OIDC_CLIENT_SECRET}
id_token_signed_response_alg: ES256指定できる値として設定ページが挙げているのは、既定のRS256に加えてES256、PS256、EdDSAです。HS256のような共有鍵方式を使えるかどうかは、設定ページに記載がありません。
署名方式とクライアント認証は別の設定
混同しやすいのが、private_key_jwtとの関係です。id_token_signed_response_algは、IdPがゲートウェイへ返すid_tokenの署名を検証するための期待値です。ゲートウェイがIdPのトークンエンドポイントへ自分を証明するときの署名は別物で、こちらはRS256で署名されると設定ページに書かれています。後者の手順はprivate_key_jwtの設定記事にあります。
2つの署名は向きが逆
id_token_signed_response_alg
IdPがid_tokenに付ける署名の方式を、ゲートウェイが検証時に期待する値として指定します。
private_key_jwt
ゲートウェイがトークンエンドポイントで認証に使うJWTの署名です。方式はRS256で、oidc側のキーでは変えられません。
設定ページの証明書認証の節は、Microsoft Entraの証明書資格情報を例に挙げています。token_endpoint_auth_method: private_key_jwtでトークンエンドポイントに認証しつつ、id_token_signed_response_algでid_tokenの署名方式を別に指定する、という書き方になります。2つは別々のキーです。
additional_authorized_partiesはKeycloakのどんな構成で効くのか
設定ページの説明は「client_idに加えて受け付けるazpの値。Keycloakのブローカーとtoken exchangeのフロー向け」です。ブローカーは、Keycloakが別のIdPへのサインインを仲介する構成を指します。token exchangeは、あるトークンを別のクライアント宛てのトークンに交換するKeycloakの機能です。
こうした構成では、id_tokenのazpにゲートウェイのclient_id以外の値が入ることがあります。ゲートウェイは既定でそれを受け付けないため、サインインの検証で止まります。止まったとき、受け付けたいazpの値を列挙します。
oidc:
issuer: https://keycloak.example.com/realms/corp
client_id: claude-gateway
client_secret: ${OIDC_CLIENT_SECRET}
additional_authorized_parties:
- broker-frontend値の型は、設定ページの表に書かれていません。複数の値を足す前提の名前なので上の例は配列で書きましたが、単一の文字列でよいかは公式の記載がありません。起動時に型の検証で弾かれたら、そのエラー文言に従ってください。broker-frontendも説明用の仮の名前です。
足す値は、推測ではなく実際のid_tokenから取るのが確実です。JWTは.で区切られた3つの部分からなり、2つ目がクレームを含むペイロードです。手元でid_tokenを取れたなら、次のようにデコードしてazpを読めます。
echo "$ID_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | grep -o '"azp":"[^"]*"'base64 -dは長さの端数で失敗することがあります。その場合はJWTのデコードに対応したツールを使ってください。ログから切り分ける流れは次の節で扱います。
サインインが通らないとき、どの順で切り分けるか
/oauth/callbackが「Sign-in could not be completed」を表示したら、デプロイガイドの表では、メールドメインの拒否・id_tokenの検証失敗・email_verifiedが明示的にfalse、のどれかです。このうちid_tokenの検証失敗が、署名方式とazpの2キーに該当します。設定ページには、署名方式やazpの不一致に固有のエラー文言は載っていません。次の順に消していくのが現実的です。
id_token検証で止まるときの切り分け
- Qログに issuer や discovery の取得エラーが出ているか?答えがいいえ答えがはい
issuerに届かない、または証明書チェーンが信頼されていない状態です。ca_cert_pem、プロキシ経由ならuse_proxyを見直します。 - QIdPのクライアント設定の署名方式がRS256以外か?答えがいいえ答えがはい
id_token_signed_response_algにその方式を書きます。 - QKeycloakのブローカーかtoken exchangeを使っているか?答えがいいえ答えがはい
additional_authorized_partiesに、id_tokenで実際に使われているazpの値を足します。
それでも通らない
時計のずれを疑い、clock_skew_secondsを上げます。設定ページのclock_skew_secondsの行によると、既定の0は厳密な検証です。同じ行は、サインイン直後に「token expired / not yet valid」というエラーが出るなら、ホストとIdPの時計のずれを疑って値を上げるよう書いています。
ログの詳細度はCLAUDE_GATEWAY_LOG_LEVELで変えられます。debugにすると、サインインと更新のたびにid_tokenに入っているクレーム名がログに出ます。値ではなく名前だけなので、azpやgroupsが載っているかの確認に使えます。azpの中身そのものを知りたいときは、IdP側でid_tokenを発行させ、JWTをデコードしてazpを読むのが手っ取り早い方法です。
セッション更新のたびに止まる症状は、署名やazpではなく別の原因のことがあります。ログイン後にCloud gateway session expiredが繰り返し出る場合は、ログインエラー3種の記事の切り分けが先です。
Keycloakで一緒に確認したい4つのキー
KeycloakをIdPにするときは、署名方式とazp以外にも、設定ページが名指しでKeycloakを挙げているキーがあります。
| キー | Keycloakでの使いどころ |
|---|---|
userinfo_fallback | Keycloakでの使いどころ軽量アクセストークンではid_tokenにemailやgroupsが載らないため、/userinfoから補う。既定はfalse |
ca_cert_pem | Keycloakでの使いどころ社内PKIの背後のKeycloakやDexに接続する。パスではなくPEMの中身を書き、ファイルなら${file:/etc/gateway/idp-ca.pem}で読み込む |
discovery_url | Keycloakでの使いどころissuerのホストを書き換えるプロキシの背後にあるとき、ディスカバリー文書の取得先を別URLにする。パスに/.well-known/が必要 |
use_proxy | Keycloakでの使いどころゲートウェイからIdPへの通信をフォワードプロキシ経由にする。v2.1.227以降 |
ca_cert_pemは、システムの信頼ストアを置き換えます。IdPへのリクエストにだけ効くので、推論用のアップストリームへの通信には影響しません。プロキシ経由だけの環境でIdPに届かせる設定は、proxy経由だけの環境の記事で扱っています。
2つのキーを同じgateway.yamlに書くとどうなるか
署名方式とazpの両方が食い違う構成もあります。たとえばKeycloakがES256で署名し、ブローカー経由でazpが別の値になる場合は、次のように2行を同じoidcブロックに並べます。2つは独立したキーなので、片方だけ書いても、両方書いても動作の意味は変わりません。
oidc:
issuer: https://keycloak.example.com/realms/corp
client_id: claude-gateway
client_secret: ${OIDC_CLIENT_SECRET}
id_token_signed_response_alg: ES256
additional_authorized_parties:
- broker-frontend
clock_skew_seconds: 30書く値は、どこから取るかが違います。
| gateway.yamlのキー | 書く値 | 値の出どころ |
|---|---|---|
id_token_signed_response_alg | 書く値ES256など | 値の出どころIdPのクライアント設定にある署名方式 |
additional_authorized_parties | 書く値broker-frontendなど | 値の出どころ実際のid_tokenをデコードして読んだazp |
client_id | 書く値claude-gatewayなど | 値の出どころIdPに登録したOAuthクライアントのID |
clock_skew_seconds | 書く値30など | 値の出どころホストとIdPの時計のずれを許す秒数 |
clock_skew_secondsの30は説明用の値です。設定ページは既定の0が厳密であることだけを書いており、推奨値は示していません。
Google WorkspaceのグループをDirectory APIで引くgoogle_groupsは、KeycloakのazpやES256とは別系統の設定です。
まとめ
署名方式とazpは、IdPが標準の挙動から外れたときだけ触るキーです。症状から逆算するなら、署名方式の不一致はIdPのクライアント設定を見てid_token_signed_response_algを、azpの不一致はKeycloakのブローカーかtoken exchangeを疑ってadditional_authorized_partiesを使います。どちらにも固有のエラー文言が公式にないので、debugログとJWTのデコードで事実を取ってから書く順番が安全です。
全体の構成から確かめたいときは、Claude apps gatewayの使い方に戻ると流れがつかめます。