Claude API Workload Identity Federation を使って Cloud Run からClaude APIキーなしでClaudeを動かしてみた

Claude API Workload Identity Federation を使って Cloud Run からClaude APIキーなしでClaudeを動かしてみた

2026.05.06

こんにちは、せーのです。

先日、新機能として 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 ステップです。

  1. 実行環境(今回は Cloud Run)が IdP の JWT を取得
  2. JWT を Anthropic の /v1/oauth/token で交換
  3. 返ってきた sk-ant-oat01-...Authorization: Bearer で使う

違いを整理すると、API キーは「長期の秘密情報を置いておく」方式WIF は「短命 JWT をその都度交換する」方式、というイメージです。

前回までとのつながり

この連載の整理は次のとおりです。

  • 第一弾: GitHub Actions OIDC を使った WIF
  • 前回: AWS Lambda + STS を使った WIF
  • 今回: Google Cloud Run + metadata server を使った WIF

それぞれ JWT の取り方は違いますが、Anthropic 側の交換フローは同じです。

全体アーキテクチャ

google-cloud-wif-deskt.png

今回作るものは大きく 4 つです。

  1. GCP 側のサービスアカウント
  2. Anthropic 側の Issuer / Service Account / Federation Rule
  3. Cloud Run の Python 実装
  4. デプロイと実行確認

やってみた(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.com
  • audience: https://api.anthropic.com
  • claims.sub: 先ほどの uniqueId
  • claims.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.subclaims.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/tokeninvalid_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 キーなしで呼び出しできることを確認しました。

今回の学びを要点だけ並べると次のとおりです。

  1. Cloud Run では SA を正しく付与すれば、実装はシンプルに組める
  2. GCP は claims.sub + claims.email の exact 一致が扱いやすい
  3. AWS は事前設定と subject_prefix 設計が特に重要
  4. どの環境でも audience と認証優先順位の確認が最後の品質を決める

そして「API キーを安全に置くにはどうするか」から、「そもそも長期キーを置かない」に発想が変わるのは、運用の体験としてかなり大きいと感じました。

Github Actions編・AWS 編・今回の Cloud Run 編まで通して見ると、WIF の全体像がぐっと掴みやすくなるはずです。
まずは小さなスモークテストから試してみてください。

参考資料


生成AI活用はクラスメソッドにお任せ

過去に支援してきた生成AIの支援実績100+を元にホワイトペーパーを作成しました。御社が抱えている課題のうち、どれが解決できて、どのようなサービスが受けられるのか?4つのフェーズに分けてまとめています。どうぞお気軽にご覧ください。

生成AI資料イメージ

無料でダウンロードする

この記事をシェアする

関連記事