Skip to main content
A GitHub Actions job exchanges its OIDC token for a short-lived theAuth access token, with no stored client secret.
Go only. The TypeScript package has no GitHub helper, and its jwtBearer grant expects an ID-JAG assertion (typ: oauth-id-jag+jwt, a client_id claim and aud equal to the issuer). A raw GitHub OIDC token is refused. The full Go write-up is in go/docs/github-actions-oidc.md.
1

Request the job’s OIDC token

Grant id-token: write and request a token for your audience. GitHub signs claims that name the repository, branch, environment and workflow.
.github/workflows/deploy.yml
2

Exchange it for an access token

Post the token to the theAuth token endpoint with the RFC 7523 jwt-bearer grant. Add client_id=<agent client id> to make the token act as an agent, so the agent’s policy and rate limit apply.
3

Restrict the issuer (Go)

GitHubActionsIssuer refuses to build an issuer with no restriction. Each of Repository, RepositoryOwner, Refs and Environment adds a matcher, and all matchers must pass. SubjectMapper maps GitHub’s sub to a local user, because the built-in mapper expects a ULID.
If Audience is empty, the required aud is your authorization server issuer URL.
4

Check a denial

A workflow that fails any matcher gets invalid_grant with the same description every time. Test it by running the workflow from a branch other than main. The operator sees the failed claim names in the audit log:

What is enforced

Claim names come from GitHub’s live discovery document.
Last modified on October 17, 2026