GitHub Actions
A GitHub Actions job can publish without a stored secret. The job gets an identity token from GitHub. Telesphorepo trades it for a publish session.
Set up
Section titled “Set up”-
An admin opens the Trust policies page and picks the GitHub Actions preset. The issuer is
https://token.actions.githubusercontent.com. -
Add claims that name your workflow. Every claim needs at least one allowed value. A value is exact text or a pattern with
*.Claim Example repositoryacme/apprefrefs/tags/v*workflow_refacme/app/.github/workflows/release.yml@* -
List the repositories that the policy may publish to.
-
Give the job
permissions: id-token: write.
Leave the audience empty. It then means the base_url of the server. See Trust policies for all fields.
Publish with the plugin or extension
Section titled “Publish with the plugin or extension”With the Gradle plugin or the Maven extension, you add no step. They find the GitHub token on their own.
name: releaseon: push: tags: ["v*"]
permissions: id-token: write contents: read
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 - run: ./gradlew publishPublish with curl
Section titled “Publish with curl”Without a client, use two curl calls. The examples need jq.
permissions: id-token: write contents: read
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Publish run: | OIDC=$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://repo.acme.com" | jq -r .value) SESSION=$(curl -sSf https://repo.acme.com/api/publish/sessions/oidc \ -H 'Content-Type: application/json' \ -d "{\"token\":\"$OIDC\",\"repo\":\"releases\"}") TOKEN=$(echo "$SESSION" | jq -r .token)
curl -sSf -u "ci:$TOKEN" -T app-1.0.jar \ https://repo.acme.com/maven/releases/com/acme/app/1.0/app-1.0.jar
# End the session when the publish is done. curl -sSf -X DELETE -u "ci:$TOKEN" \ "https://repo.acme.com/api/publish/sessions/$(echo "$SESSION" | jq -r .id)"The first call asks GitHub for a token. The audience must equal the audience of the trust policy. The second call trades the token for a session. The session answer has id, token, repo and expires_at. Use token as the Basic password with any user name.
Gotchas
Section titled “Gotchas”- Without
id-token: write, GitHub gives the job no token request URL, and the exchange fails. - The server checks signature, issuer, audience and expiry. The token must be at most 10 minutes old.
- The server accepts each identity token once. Do not retry the exchange with the same token. Ask GitHub for a new one.
- If no enabled policy matches the issuer, audience and claims, or the policy does not list the repository, the exchange fails.
- A
*in a claim value matches any text,/included.refs/tags/v*matchesrefs/tags/v1/x. - Narrow the
refandworkflow_refclaims so that only your release workflow matches. - If you edit or disable a policy, the server ends its open sessions.