Skip to content

Fetch organization repositories with Workload Identity - #238

Merged
ericmj merged 1 commit into
mainfrom
workload-identity-fetch
Oct 10, 2026
Merged

ericmj merged 1 commit into
mainfrom
workload-identity-fetch

Conversation

@ericmj

@ericmj ericmj commented Oct 9, 2026

Copy link
Copy Markdown
Member

A repository request to a Hex.pm organization that resolves no other credentials now exchanges the CI job's OIDC token for a repository:ORG token. hexpm issues those to organization workload identities with the read or write role since hexpm/hexpm@a746876. Organization keys, HEX_REPOS_KEY and a user session still come first.

Build tools opt in with two new optional callbacks, get_workload_identity_token and persist_workload_identity_token, which keep the outcome of the last exchange for each repository in memory. Without them every request would exchange a new OIDC token, so a build tool that doesn't provide them resolves repository credentials as before.

  • The exchange holds the repo lock, so concurrent requests share one token. A token is reused until it's within five minutes of expiring, and a token_expired 401 exchanges again unless another request already replaced the rejected token.
  • A failed exchange is kept and not repeated, since hexpm counts failed exchanges against a limit per GitHub repository.
  • After a failed exchange an optional request runs without credentials, as it would outside CI, so a mirror that authenticates another way still works. A 401 or 403 to that request, or a failed renewal, returns {error, {auth_error, {workload_identity_failed, Reason}}}.
  • The jwt-bearer exchange now requires expires_in in the token response, which hexpm always sends. This applies to workload_identity_auth/2 too.

A repository request to a Hex.pm organization that resolves no other
credentials now exchanges the CI job's OIDC token for a token scoped to
the organization's repository (repository:ORG), which hexpm issues to an
organization workload identity with the read or write role.

Build tools opt in by providing the new get_workload_identity_token and
persist_workload_identity_token callbacks, which keep the outcome of the
last exchange for each repository. Without somewhere to keep it every
request would exchange a new OIDC token, so a build tool without them
resolves repository credentials as before.

A kept token is reused until it's within five minutes of expiring, and
the exchange holds the repo lock so concurrent requests share one. A
token_expired 401 exchanges again, unless another request already
replaced the rejected token.

A failed exchange is kept too, so it isn't repeated for every request:
hexpm counts each failed exchange against a limit per GitHub repository.
The workload identity was only picked up from the CI job, so with
optional set the request then runs without credentials, as it would
outside CI, and a mirror that authenticates another way still works. A
401 or 403 to that request, or a failed renewal, returns
{error, {auth_error, {workload_identity_failed, Reason}}}.

The jwt-bearer exchange now requires expires_in in the token response,
which hexpm always sends.
@ericmj
ericmj marked this pull request as ready for review October 9, 2026 20:28
@ericmj
ericmj merged commit d7ad55b into main Oct 10, 2026
10 checks passed
@ericmj
ericmj deleted the workload-identity-fetch branch October 10, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant