Workload Identity
Configure workload identity rules to let CI workloads authenticate with Develocity using short-lived OIDC tokens instead of long-lived access keys.
Overview
Workload identity federation builds on the OpenID Connect (OIDC) standard. Instead of storing a long-lived Develocity access key, you exchange a short-lived OIDC token from your CI provider for a Develocity access token.
The exchange works in these steps:
-
The CI provider issues a short-lived OIDC token at the start of the job. A GitHub Actions token lasts about five minutes.
-
The CI integration exchanges the token for a Develocity access token via a
POST /api/auth/tokenrequest. -
The build authenticates with the returned Develocity access token for the rest of the run.
Your CI job mints the OIDC token from your CI provider. No Develocity plugin mints it for you.
You supply that token in place of your access key, in host-qualified form. The CI integration exchanges it for a Develocity access token the same way it exchanges an access key.
For how each provider mints and supplies the token, see the provider examples below and the Continuous Integration section.
Because the CI provider issues a fresh, short-lived OIDC token for each job, there is no shared secret to store, rotate, or leak. Claim requirements let you scope a rule to a specific organization, repository, branch, or workflow, so each workload receives only the access it needs.
Use workload identity when your CI provider issues OIDC tokens and you want to remove long-lived Develocity access keys from CI configuration. Also use it when you want to grant roles based on which repository, branch, or workflow is authenticating.
|
You can present the OIDC token directly in place of an access key, but it usually expires partway through a build. That is why the CI integration exchanges it for a longer-lived Develocity access token. On a CI system with no integration that performs the exchange, see Other CI Systems for the direct path. |
Prerequisites
-
An account with permission to configure access control in Develocity.
-
A CI provider that issues OIDC tokens (GitHub Actions, Google Cloud Workload Identity, HashiCorp Vault, and similar).
-
Network connectivity from Develocity to the issuer’s JWKS endpoint (see Issuer Key-Refresh Failures).
Configure via the User Interface
Navigate to and select Add.
|
Develocity evaluates each incoming token against every configured rule for its issuer. A rule matches when the issuer, audience, and all claim requirements match. If multiple rules match, Develocity applies all of them and grants the union of their roles and project groups. |
-
ID: Enter a name for the rule, up to 256 characters, that no other rule uses. The ID identifies the rule in the rule list and on its edit page. The token never contains it, and Develocity does not match it against the token. Name it for the workload it admits, such as
github-acme-app-main, rather thanrule-1. You can rename a rule later. -
Issuer: Enter the
issclaim value from the provider’s tokens (for example,https://token.actions.githubusercontent.comfor GitHub Actions). Select Test to verify Develocity can reach the issuer’s JWKS endpoint before saving. If Develocity cannot reach the JWKS endpoint, see Issuer Key-Refresh Failures. -
Audience: Enter the
audclaim value the token must carry. If your CI provider lets the job choose the audience, use your Develocity server URL, such ashttps://develocity.example.com. If the provider sets a fixed audience, enter the value its tokens carry. See Choosing the Audience Value. -
Assigned roles and Assigned project groups: Select the roles and project groups to grant to every token that matches this rule.
-
Roles claim and Project groups claim: Optionally enter a claim name for dynamic grants. At authentication time, Develocity reads that claim from the token. It matches the claim’s values against the identity provider (IdP) attribute values configured on each role or project group. Both string and list claims are supported: a list-valued claim grants all roles or project groups whose IdP attribute value matches any element.
After you save the rule, Develocity opens the claim requirements screen. Add one or more claim requirements that must all match. Available match types are Equals, Starts With, Ends With, Contains, and Regular Expression.
|
A rule with no claim requirements never matches any token. Add at least one claim requirement before the rule can authenticate a workload. If you truly need to accept tokens with no other claim criteria, you can add a requirement for the issuer or audience claim. |
Each rule also carries Read isolation level and Write isolation level under Universal Cache isolation, which Develocity shows only while trusted artifact isolation is on. See Universal Cache Isolation.
|
Save stores the rule as a draft without applying it. Develocity then shows a banner at the top of the page: There are saved configuration changes that have not been applied. Select Apply in that banner to put the rule into effect. If you leave the page or select Discard first, you lose the change. Develocity does not restart when you apply a workload identity change. |
Claim Requirement Match Types
| Match Type | Behavior |
|---|---|
Equals |
Claim value must equal the specified string. |
Starts With |
Claim value must begin with the specified string. |
Ends With |
Claim value must end with the specified string. |
Contains |
Claim value must include the specified string. |
Regular Expression |
Claim value must match the regular expression. |
Starts With, Ends With, and Contains compare the requirement string against the claim value as literal text.
The boundary character is not a special or stripped token.
It is the character that bounds a segment in the claim’s own format, matched as part of the required string.
For example, the / in a Jenkins job URL and the @ in a GitHub workflow ref both follow from the claim’s own format.
Any character can serve as the boundary as long as it naturally ends a segment in the real claim value.
|
Include the boundary character at the end of a Starts With string (or the start of an Ends With string), or the requirement over-matches.
A Starts With requirement of |
Choosing Claim Requirements
Claim requirements decide which workloads a rule admits, so build them from values that a workload cannot choose for itself.
On a shared issuer such as GitHub.com or GitLab.com, any repository can request a token with your audience.
Always require your organization or namespace there, and require it on its ID claim, such as repository_owner_id or namespace_id, rather than on its name.
After a rename or deletion, someone else can register the freed name, and a requirement on the name then admits their workloads.
A single-tenant issuer, such as Jenkins or a self-managed GitLab instance, serves your own CI system and no one else.
It does not need this requirement, because it issues tokens to your own workloads alone.
Whoever can create repositories, branches, or jobs chooses the other names in a token. Those names are the repository, project path, branch or ref, workflow path, or Jenkins job URL. Prefer an ID claim where one exists. An Equals requirement on a name is an acceptable trade for readability when the same rule pins the organization by ID. That is safe because no one outside your organization can create names under it. A branch requirement can be no stronger than the control over who can push to that branch, so protect the branch in your CI provider.
Use Equals wherever you can.
Starts With, Ends With, Contains, and Regular Expression accept any value crafted to fit them.
For example, a repository Ends With /acme-app requirement also matches evil/acme-app, a repository anyone can create.
A Regular Expression must match the entire claim value, and . in it matches any character, so escape a literal dot as \..
Choosing the Audience Value
The audience the CI job requests becomes the token’s aud claim, and it must match the rule’s Audience character for character.
Develocity does not normalize case, scheme, or a trailing slash, so a token issued for https://develocity.example.com/ does not match a rule whose Audience is https://develocity.example.com.
If your CI provider lets the job choose the audience, use your Develocity server URL. Give the scheme and hostname, include the port only if it is not the default, and omit any path or trailing slash. Do not rely on the audience a provider assigns when the job requests none. GitHub Actions, for example, defaults to the repository owner URL, which other services that trust your organization’s tokens also accept.
If the provider sets a fixed audience, enter the value its tokens carry as the rule’s Audience.
The audience stops anyone from replaying a token issued for another service against Develocity. It does not limit who can get a token: on GitHub.com or GitLab.com, any repository can request one with your audience. Claim requirements are what restrict access, as Choosing Claim Requirements explains.
Provider Examples
Each provider emits its own set of claims. Configure the issuer and audience, then add claim requirements that scope the rule to an organization, a repository, or a branch, following Choosing Claim Requirements. Develocity reads each required claim as a string, so scope on string-valued claims alone. Develocity treats numeric, boolean, and array claims as absent, and the requirement fails.
GitHub Actions
GitHub Actions tokens use the issuer https://token.actions.githubusercontent.com.
Pass your Develocity server URL to getIDToken() and enter the same value as the rule’s Audience.
Calling getIDToken() with no argument issues the token for the repository owner URL, such as https://github.com/acme, which is not specific to Develocity.
Scope on the dedicated string claims repository_owner_id, repository_owner, repository_id, repository, ref, workflow_ref, and job_workflow_ref.
GitHub emits repository_owner_id and repository_id as string values that survive renames.
The organization row uses repository_owner_id for that reason, and you can use repository_id with Equals in place of repository when rename safety matters more than a readable rule.
Your organization’s ID is the id field returned by https://api.github.com/orgs/acme.
Combine every other row with the organization row, so that each rule admits your organization’s repositories and no others.
| Goal | Claim requirement(s) |
|---|---|
Limit to an organization |
|
Limit to a single repository |
|
Limit to a single branch |
|
Limit to a single workflow file |
|
Limit to a shared reusable workflow |
|
The workflow_ref and job_workflow_ref claims both use the format {owner}/{repo}/.github/workflows/{file}@{ref}.
workflow_ref identifies the top-level workflow that started the run.
Scoping on it pins authentication to one workflow file and rejects every other workflow in the repository.
job_workflow_ref identifies the reusable workflow the job actually calls.
A central repository can then own the build logic and authorize the jobs that run it, and no others.
Any repository can call a public reusable workflow, so the job_workflow_ref requirement alone admits callers from other accounts.
Keep the repository_owner_id requirement, which identifies the calling repository’s owner.
Include the trailing @ when scoping to a file regardless of ref, or use Equals with a full @{ref} suffix to pin a specific ref.
Do not scope on sub.
GitHub gives repositories created after 2026-07-15 an immutable sub that embeds numeric owner and repository IDs.
A sub Starts With rule written for the older format then stops matching with no warning.
The claims ref_type (branch or tag) and environment are also string-valued if you need to narrow further.
For the workflow setup that mints and supplies the token, see Authenticate With Workload Identity. For the underlying GitHub mechanism, see the GitHub Actions OpenID Connect documentation.
GitLab CI
GitLab CI tokens use the GitLab instance URL as the issuer, such as https://gitlab.com or https://gitlab.example.com.
Set aud: in the job’s id_tokens block to your Develocity server URL, and enter the same value as the rule’s Audience.
Scope on the string claims namespace_id, namespace_path, project_id, project_path, and ref.
On GitLab.com, combine every other row with the namespace row, so that each rule admits your group’s projects and no others.
| Goal | Claim requirement(s) |
|---|---|
Limit to a group or namespace |
|
Limit to a single project |
|
Limit to a single branch |
|
To also exclude tag pipelines, add ref_type Equals branch.
GitLab emits project_id and namespace_id as string values that survive renames.
The namespace row uses namespace_id for that reason, and you can use project_id with Equals in place of project_path when rename safety matters more than a readable rule.
For the job setup that mints and supplies the token, see Authenticate With Workload Identity. For the underlying GitLab mechanism, see the GitLab ID token authentication documentation.
Jenkins
The OpenID Connect Provider plugin makes Jenkins an OIDC issuer.
Its issuer is your Jenkins URL followed by /oidc, such as https://jenkins.example.com/oidc.
Set the audience on the OpenID Connect ID Token credential in Jenkins to your Develocity server URL.
Enter the same value as the rule’s Audience.
By default the plugin sets sub to the job URL (JOB_URL) and a numeric build_number that Develocity cannot match.
It exposes no repository or branch claims, so scope on sub.
JOB_URL always ends in a trailing slash, and for a multibranch pipeline the branch is a child job whose name is percent-encoded, such as https://jenkins.example.com/job/acme-app/job/main/.
| Goal | Claim requirement(s) |
|---|---|
Limit to all jobs in a folder |
|
Limit to a single job |
|
Limit to all branches of a multibranch pipeline |
|
Limit to a single branch |
|
Always include the trailing slash on an Equals value.
To scope on anything other than the job URL, go to and add a custom claim template that exposes a claim such as branch.
Admins who store the credential on a folder can use the folder-scoped issuer, which appends the folder path to the issuer, such as https://jenkins.example.com/oidc/job/acme-team.
For the pipeline setup that binds and supplies the token, see Authenticate With Workload Identity.
Universal Cache Isolation
The Workload identity page also carries the controls for trusted artifact isolation. Trusted artifact isolation splits every Universal Cache key into a trusted entry and an untrusted one. They live here because isolation levels come from claim matching. You can assign them only to a workload identity rule, never to an access key.
Under Universal Cache isolation:
-
Enable Universal Cache isolation turns isolation on for the installation. It is off by default, and upgrading does not turn it on.
-
Read isolation level and Write isolation level on each rule decide which of the two entries a matching workload writes, and which it can read. Develocity hides these per-rule controls while isolation is off.
Enabling isolation changes cache behavior for every workload, and the order in which you enable it and set the levels matters. Trusted Artifact Isolation explains what each level does, the one combination Develocity refuses, which levels suit each kind of workload, and how to turn isolation on.
Troubleshooting
Most problems reported against a workload identity rule have one of three causes. Develocity cannot reach the rule’s issuer, the token’s audience does not match the rule, or the rule’s isolation levels produce cache behavior you did not expect.
Issuer Key-Refresh Failures
Develocity displays an admin notice when it cannot refresh signing keys from a configured issuer. Develocity fetches signing keys (JWKS) from each configured issuer and retries every minute if the fetch fails. Develocity shows the admin notice once an issuer has been unreachable for 15 minutes. After 24 hours without a successful refresh, Develocity drops the cached keys and token validation for that issuer fails, breaking any builds that use it.
To diagnose:
-
Navigate to . The rule list shows the JWKS status per issuer.
-
Select Test on the affected rule to attempt a live fetch and see the error.
-
Check Develocity server logs for WARN-level messages from
WorkloadIdentityRegistry.
Common causes:
-
The OIDC configuration endpoint at
{issuer}/.well-known/openid-configurationis unreachable from the Develocity host (firewall, egress proxy, or DNS). -
The JWKS endpoint (from the
jwks_urifield in the discovery document) is unreachable. -
The issuer URL is wrong: typo, trailing-slash mismatch, or HTTP instead of HTTPS.
-
A TLS trust failure: self-signed or expired certificate, or missing intermediate CA.
|
Per the OpenID Connect Discovery 1.0 spec, the issuer must be an HTTPS URL with no query string or fragment.
It must also match the Develocity fetches |
Fix the issuer URL or network path, or add the CA certificate to Develocity’s trusted store if TLS is the cause. Develocity retries every minute and clears the notice once a fetch succeeds.
Audience Mismatch
Sometimes a build fails to authenticate even though the issuer’s JWKS status is healthy and the claim requirements look correct.
In that case, compare the token’s audience with the rule’s.
Develocity skips a rule whose Audience does not appear in the token’s aud claim.
Develocity rejects a token that matches no rule as an invalid credential.
The error does not say whether the audience or a claim requirement failed.
Common causes:
-
The CI job requests no audience, so the token carries the provider’s default, such as the repository owner URL on GitHub Actions.
-
The two values differ by a trailing slash, the scheme, the port, or a path.
-
Someone copied the job from another installation, and it still requests that server’s URL.
To see what the token carries, decode its payload in the CI job and print the aud claim alone, never the token itself.
Change whichever side is wrong so the two strings are identical, following Choosing the Audience Value.
Unexpected Cache Behavior
Trusted artifact isolation decides what a build can read and write from the levels on the rule its credentials matched.
A mistake in those levels therefore surfaces as a cache problem rather than an authentication one.
Three symptoms share that shape: a build that starts missing every cache key, a cache response of 409 Conflict carrying an X-Cache-Isolation-Conflict header, and a job whose Artifact Cache summary reports zero trusted entries.
In each, the token validated and the rule matched, and the isolation levels alone are wrong.
Diagnose these from Trusted Artifact Isolation, which gives the cause and the remedy for each.