Lesson 9 · Security, supply chain & operations

Keyless GCP auth (OIDC/WIF)

How CI proves who it is to Google Cloud — with no static keys to leak.

Your win: explain how a GitHub Actions job authenticates to GCP without a service-account key file — the OIDC / Workload Identity Federation token exchange — and why this is the single biggest CI security win.

The problem: long-lived keys leak

CI must authenticate to GCP to push images and deploy. The old way: store a service-account JSON key as a GitHub secret. But a static key is a standing liability — it doesn't expire, and if it leaks (a log, a fork, a compromised action) an attacker has your cloud until someone notices and rotates it. The modern fix is to have no key at all.1

The token exchange — OIDC / Workload Identity Federation

Instead of a stored key, a job asks GitHub to mint a short-lived OIDC token that describes it (which repo, which workflow, which branch). GCP is configured to trust GitHub's issuer and exchange that token for a short-lived Google credential.2

job (permissions: id-token: write) │ 1. request an OIDC token from GitHub ▼ GitHub mints a signed JWT ("repo=manabie-com/backend, workflow=…, ref=…") │ 2. present it to Google's STS ▼ GCP Workload Identity Federation verifies the issuer + claims │ 3. exchange for a short-lived Google credential ▼ job can now push/deploy — no static key, token expires in minutes
The one permission that unlocks it A job must declare permissions: id-token: write to be allowed to request an OIDC token. Miss that and the auth step fails with a confusing error. It's the single line that says "this job may prove its identity to an external system." Every deploy/build job here sets it.
Anchor — the auth step & per-purpose bots Jobs declare permissions: id-token: "write" (tiered.pre_merge.yml:152-156, tbd.deploy.yml:362-364), then use google-github-actions/auth@v2 (.github/actions/tbd.build-backend/action.yml:49-53) with a WIF provider (projects/…/workloadIdentityPools/gh-action-pool/providers/build-bot-provider) and a purpose-specific service account. And there's a nice least-privilege detail: different bots for different jobsprod-build-bot@student-coach-e1e95 for builds, a deploy SA resolved per env/org (auth-oidc/action.yaml:84-87), an integration-test-bot@… for E2E. So a compromised build job can't also deploy prod — it simply isn't that identity. No static SA JSON keys anywhere.
You've seen this idea before This is the same trust mechanism as Course 2 L12 / Course 4 L9's Workload Identity — there, a Pod's ServiceAccount let it reach Google APIs without keys. Here, a GitHub job's identity does. Same principle (federate an external identity to GCP), two contexts.
Read this next

GitHub OIDC + google-github-actions/auth

How the OIDC token is minted and exchanged, and the auth action's config.

docs.github.com — OIDC in Google Cloud
github.com — google-github-actions/auth

Check yourself (from memory)

Q1. Keyless auth to GCP works by…

GitHub mints an OIDC token; GCP's WIF exchanges it for a short-lived credential. No static key.

Q2. A job must declare which permission to use OIDC?

id-token: write lets the job request an OIDC token; without it the auth step fails.

Q3. Using different bot SAs per job (build/deploy/test) gives…

A compromised build job isn't the deploy identity, so it can't deploy prod. Least privilege by design.
Keyless GCP auth — the exchange, the permission, and the repo's bots.
recall, then click to reveal
PROBLEM: static SA JSON keys don't expire and leak → standing liability. FIX (OIDC / WORKLOAD IDENTITY FEDERATION): a job (permissions: id-token: write) asks GitHub for a short-lived OIDC token describing it (repo/workflow/ref); GCP TRUSTS GitHub's issuer + EXCHANGES it (via STS) for a short-lived Google credential — NO static key. Repo: google-github-actions/auth@v2 with a WIF provider (.../gh-action-pool/providers/build-bot-provider) + PER-PURPOSE bots (prod-build-bot for build, deploy SA per env/org, integration-test-bot for E2E) = LEAST PRIVILEGE (a build job can't deploy prod). Same principle as Pod Workload Identity (C2 L12 / C4 L9), applied to GitHub jobs.
Want to see how WIF attribute-mapping restricts which repo/branch may assume a bot, or the STS exchange in detail? Ask me.

1. Google — Keyless auth from GitHub Actions.

2. GitHub — OIDC in Google Cloud; google-github-actions/auth.