Skip to content
Select theme
  • Auto
  • Dark
  • Light

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.

  1. An admin opens the Trust policies page and picks the GitHub Actions preset. The issuer is https://token.actions.githubusercontent.com.

  2. 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
    repository acme/app
    ref refs/tags/v*
    workflow_ref acme/app/.github/workflows/release.yml@*
  3. List the repositories that the policy may publish to.

  4. 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.

With the Gradle plugin or the Maven extension, you add no step. They find the GitHub token on their own.

.github/workflows/release.yml
name: release
on:
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 publish

Without a client, use two curl calls. The examples need jq.

.github/workflows/release.yml
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.

  • 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* matches refs/tags/v1/x.
  • Narrow the ref and workflow_ref claims so that only your release workflow matches.
  • If you edit or disable a policy, the server ends its open sessions.