One assertion, two consumers.

An OIDC JWT is a signed assertion. What changes is who validates it and what they do with it. Two tracks follow the same class of token to two very different validators: a cloud token service that exchanges it for credentials, and an API server that decides whether to trust it at all.

Track 1 · a machine

Workload identity federation

Workload identity federation, end to end A Forgejo CI runner with no Google Cloud credentials gets a signed OIDC token from the Forgejo issuer, exchanges it at Google's Security Token Service, which verifies it against the issuer's public keys, impersonates a service account through IAM Credentials, and calls the Cloud Storage API with the service account's token. forgejo google cloud verifies via JWKS 1 mint OIDC token 2 exchange 3 impersonate SA 4 call the API issuer forgejo.authlab.app runner ci job · no gcp keys sts sts.googleapis.com iam credentials iamcredentials.googleapis.com cloud storage storage.googleapis.com

Track 2 · a human

Workforce identity federation

Workforce identity federation, end to end On a human's laptop, kubectl's kubelogin plugin signs the human in to Okta with PKCE and receives an id_token. kubectl presents it to the kube-apiserver, which validates it with its own OIDC flags, fetching Okta's public keys, then resolves the prefixed user and groups through RBAC. okta kubernetes cluster verifies via JWKS 1 PKCE sign-in, id_token 2 present id_token 3 resolve identity okta dev-12345678.okta.com kubectl kubelogin · a human's laptop kube-apiserver validates with its --oidc-* flags rbac okta: user and groups → bindings