
Claude API Workload Identity Federation を使って Cloud Run からClaude APIキーなしでClaudeを動かしてみた
こんにちは、せーのです。
先日、新機能として Claude API Workload Identity Federation(以下 WIF)の第一弾として、GitHub Actions から API キーなしで Claude API を呼び出しました。
Claude API Workload Identity Federation(第一弾)
続いて同じくWIFを使ってAWS LambdaからAPIキーなしのClaude呼び出しを行いました。
Claude API WIFをAWS Lambdaで試した記事
今回は第三弾として、Google Cloud Run から API キーなしで Claude API を呼ぶところをやってみます。
さらに、前回までの流れを踏まえて、Google Cloud で WIF を使うときに詰まりやすいポイントも Tips として多めにまとめます。
今回のゴール
今回の流れです。
- Cloud Run のサービスアカウントで Google の Identity Token を取得する
- そのトークンで Anthropic の短命アクセストークンを取得する
ANTHROPIC_API_KEYなしで Claude API を呼び出す- レスポンスとして固定文が返ることを確認する
今回はスモークテストとして、次の固定文を返すようにしています。
Anthropic WIF on Cloud Run smoke test OK.
Workload Identity Federationとは(おさらい)
WIF はざっくり言うと、「実行環境が今その場で取った短命 JWT を、Claude API 用の短命トークンに交換して使う」仕組みです。
流れは次の 3 ステップです。
- 実行環境(今回は Cloud Run)が IdP の JWT を取得
- JWT を Anthropic の
/v1/oauth/tokenで交換 - 返ってきた
sk-ant-oat01-...をAuthorization: Bearerで使う
違いを整理すると、API キーは「長期の秘密情報を置いておく」方式、WIF は「短命 JWT をその都度交換する」方式、というイメージです。
前回までとのつながり
この連載の整理は次のとおりです。
- 第一弾: GitHub Actions OIDC を使った WIF
- 前回: AWS Lambda + STS を使った WIF
- 今回: Google Cloud Run + metadata server を使った WIF
それぞれ JWT の取り方は違いますが、Anthropic 側の交換フローは同じです。
全体アーキテクチャ

今回作るものは大きく 4 つです。
- GCP 側のサービスアカウント
- Anthropic 側の Issuer / Service Account / Federation Rule
- Cloud Run の Python 実装
- デプロイと実行確認
やってみた(Cloud Run編)
1. GCP側の準備
まず API を有効化します。
gcloud services enable \
run.googleapis.com \
artifactregistry.googleapis.com \
cloudbuild.googleapis.com \
iam.googleapis.com \
--project <your-gcp-project-id>
次に Cloud Run 用のサービスアカウントを作成します。
gcloud iam service-accounts create claude-wif-cloudrun \
--project <your-gcp-project-id> \
--display-name="Claude WIF Cloud Run"
このあと Anthropic の Federation Rule で使うために、service account の uniqueId を取得します。
gcloud iam service-accounts describe \
claude-wif-cloudrun@<your-gcp-project-id>.iam.gserviceaccount.com \
--project <your-gcp-project-id> \
--format='value(uniqueId)'
2. Anthropic Console設定
今回も Anthropic 側は 3 点セットです。
- Federation Issuer(
fdis_...) - Service Account(
svac_...) - Federation Rule(
fdrl_...)
GCP でポイントになるのは次です。
issuer_url:https://accounts.google.comaudience:https://api.anthropic.comclaims.sub: 先ほどの uniqueIdclaims.email:claude-wif-cloudrun@...
ここでの重要点は、GCP では subject_prefix よりも claims の exact 一致が安全ということです。
sub が数値 ID なので、prefix でざっくり切ると意図せず広くなる可能性があります。
3. Cloud Runの実装
今回は Python SDK の WorkloadIdentityCredentials を使いました。
import os
import anthropic
import google.auth.transport.requests
import google.oauth2.id_token
from anthropic import WorkloadIdentityCredentials
from flask import Flask
app = Flask(__name__)
AUDIENCE = "https://api.anthropic.com"
def fetch_google_identity_token() -> str:
request = google.auth.transport.requests.Request()
return google.oauth2.id_token.fetch_id_token(request, AUDIENCE)
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_google_identity_token,
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
),
)
@app.route("/")
def call_claude():
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=64,
messages=[
{
"role": "user",
"content": "Respond with exactly this sentence: Anthropic WIF on Cloud Run smoke test OK.",
}
],
)
return message.content[0].text
ANTHROPIC_API_KEY は一切使っていません。
WIF 関連の ID(fdrl_... / svac_... / organization_id)だけを環境変数で渡します。
4. デプロイ
gcloud run deploy claude-wif-demo \
--source . \
--project <your-gcp-project-id> \
--region us-central1 \
--service-account claude-wif-cloudrun@<your-gcp-project-id>.iam.gserviceaccount.com \
--set-env-vars "ANTHROPIC_FEDERATION_RULE_ID=fdrl_xxx,ANTHROPIC_ORGANIZATION_ID=xxx,ANTHROPIC_SERVICE_ACCOUNT_ID=svac_xxx" \
--allow-unauthenticated \
--max-instances 1
デプロイ後にエンドポイントへアクセスして、固定文が返れば成功です。
curl https://claude-wif-demo-<hash>-uc.a.run.app/
Anthropic WIF on Cloud Run smoke test OK.
無事、Cloud Run でも API キーなしで Claude API 呼び出しが通りました。
GCPでのハマりどころ
format=full を知らないとハマる
metadata server から直接トークンを取って検証するとき、format=full を付けないと email が入りません。
# NG(email が入らない)
.../identity?audience=https://api.anthropic.com
# OK(email が入る)
.../identity?audience=https://api.anthropic.com&format=full
claims.email で rule を絞っている場合、ここがズレると invalid_grant になって、地味に原因が追いづらいです。
Service Accountを作り直すと sub が変わる
GCP の sub は SA の uniqueId なので、同名で作り直しても値は変わります。
「昨日まで動いたのに突然通らない」のパターンになります。
運用上は、claims.sub と claims.email の両方で縛るのが安全です。
AWSでWIFを使うときのTips(多め)
ここからが本題です。
前回の AWS 編とリサーチ内容を踏まえて、「ここで詰まりやすい」をまとめます。
1. Outbound web identity federation は最初に有効化
AWS では sts:GetWebIdentityToken を使う前に、アカウント設定の Outbound web identity federation を有効化する必要があります。
これを忘れると最初に次の系統で止まります。
... disabled for account ...
GCP だとこの前提作業が不要なので、同じ感覚で始めるとハマりやすいです。
2. STSはリージョナルエンドポイントを明示する
sts = boto3.client("sts", region_name="us-east-1")
ここを曖昧にすると、環境によってエラーになったり、期待しない挙動になります。
「とりあえず書いて動く」箇所ではないので、最初から固定推奨です。
3. audienceは完全一致(https://込み)
AWS でも GCP でも同じですが、ここは最重要です。
https://api.anthropic.com
api.anthropic.com(スキームなし)は別物です。
OIDC 側 aud と Anthropic Rule 側 audience が一致しないと invalid_grant になります。
4. subjectの設計は「広げすぎない」
AWS では sub が IAM Role ARN になるため、subject_prefix をロール ARN に寄せて狭く切る設計が効きます。
arn:aws:iam::<account-id>:role/claude-wif-lambda-role
最初から広い prefix で許可すると、後で「想定外の実行主体も通る」リスクが出るので、最小権限で始めるのが安全です。
5. APIキー移行時は優先順位の罠に注意
これは運用で本当に効くポイントです。
ANTHROPIC_API_KEYは federation より優先される- 空文字でもスロットを占有するケースがある
ANTHROPIC_API_KEY=""ではなく、きちんと unset する
WIF を設定したのに API キー経路で認証されていた、は普通に起きます。
移行時は「設定した」ではなく、「どの経路で認証されたか」まで確認するのが安心です。
6. 調査はレスポンス単体より Authentication history とセット
/v1/oauth/token の invalid_grant は詳細を返さないことがあります。
レスポンス本文だけで粘るより、Anthropic Console の Authentication history と合わせて見る方が早いです。
3環境の違いを一度整理
比較すると、差分はここです。
| 項目 | GitHub Actions | AWS Lambda | Cloud Run |
|---|---|---|---|
| Issuer | token.actions.githubusercontent.com |
https://<uuid>.tokens.sts.global.api.aws |
https://accounts.google.com |
| Token取得 | Actions OIDC | STS get_web_identity_token |
metadata server / google auth |
sub の型 |
repo:... |
IAM Role ARN | SA uniqueId |
| 主な罠 | audience 不一致 |
Outbound未有効化・リージョナルSTS | format=full・SA再作成時のsub変更 |
違いを整理すると、JWT の取得元はクラウドごとに違う、でも Claude 側の交換・利用フローは同じ、というイメージです。
まとめ
Claude API WIF の第三弾として、Cloud Run から API キーなしで呼び出しできることを確認しました。
今回の学びを要点だけ並べると次のとおりです。
- Cloud Run では SA を正しく付与すれば、実装はシンプルに組める
- GCP は
claims.sub + claims.emailの exact 一致が扱いやすい - AWS は事前設定と
subject_prefix設計が特に重要 - どの環境でも
audienceと認証優先順位の確認が最後の品質を決める
そして「API キーを安全に置くにはどうするか」から、「そもそも長期キーを置かない」に発想が変わるのは、運用の体験としてかなり大きいと感じました。
Github Actions編・AWS 編・今回の Cloud Run 編まで通して見ると、WIF の全体像がぐっと掴みやすくなるはずです。
まずは小さなスモークテストから試してみてください。








