Skip to content

fix(auth): resolve JWKS URL from the OIDC discovery document - #254

Merged
LouisHaftmann merged 3 commits into
devfrom
fix/jwks-oidc-discovery
Jul 29, 2026
Merged

fix(auth): resolve JWKS URL from the OIDC discovery document#254
LouisHaftmann merged 3 commits into
devfrom
fix/jwks-oidc-discovery

Conversation

@LouisHaftmann

Copy link
Copy Markdown
Collaborator

Fixes #253

Problem

When an enterprise customizes its issuer value, runner tokens carry iss: https://token.actions.githubusercontent.com/<enterpriseSlug> — but the JWKS stays at the root host. We derived the JWKS URL as {issuer}/.well-known/jwks, which 404s, so every request failed with 401 Invalid token.

Verified against the live endpoints:

  • GET https://token.actions.githubusercontent.com/<slug>/.well-known/openid-configurationjwks_uri: "https://token.actions.githubusercontent.com/.well-known/jwks" (root host, for every slug tried)
  • GET https://token.actions.githubusercontent.com/<slug>/.well-known/jwks404, matching the reporter's Expected 200 OK from the JSON Web Key Set HTTP response

Change

  • Read jwks_uri from the issuer's OIDC discovery document instead of deriving it. Resolved lazily on the first token verification and cached for the process.
  • On discovery failure: warn and fall back to the old derived URL, deliberately uncached, so a transient outage can't pin the process to a URL that may be wrong. This keeps GHES deployments (whose token hosts aren't publicly reachable to confirm they serve discovery) working exactly as before, so Compatibility with GHES #241 can't regress.
  • New optional ACTIONS_TOKEN_JWKS_URL skips discovery entirely, for deployments served by neither path.
  • The iss comparison now uses the trailing-slash-stripped issuer, so a pasted .../octocat-inc/ no longer fails the issuer check.
  • ADR-0006 amended; docs site updated separately.

An enterprise that customizes its issuer value gets tokens issued by
`https://token.actions.githubusercontent.com/<enterpriseSlug>` while the
JWKS stays at the root host, so deriving the JWKS URL as
`{issuer}/.well-known/jwks` 404s and every token fails verification with
`401 Invalid token`.

Read `jwks_uri` from `{issuer}/.well-known/openid-configuration` instead,
once and cached for the process. If discovery fails, log a warning and
fall back to the derived URL — uncached, so an outage can't pin the
process to a URL that may be wrong — which keeps GHES deployments working
as before. `ACTIONS_TOKEN_JWKS_URL` overrides both.

Fixes #253
@LouisHaftmann
LouisHaftmann force-pushed the fix/jwks-oidc-discovery branch from 10f56c4 to b2dbf48 Compare July 29, 2026 06:44
Both reader-lease cleanup tests give their `vi.waitFor` the full 5s default
test timeout, leaving nothing for the surrounding storage and database work,
so they time out on the slower driver combinations (seen on gcs + mysql on
`dev` as well). Give those two tests an explicit 30s timeout.
Merge completion locks `merge_leases` then `storage_locations`, while the
cleanup tasks lock `storage_locations` first, so mysql can pick the merge as
a deadlock victim. The `catch` then treated that transient failure as a
failed merge and reset `mergedAt`/`mergeStartedAt` to null — discarding a
merge whose `merged` object was already written, and leaving its parts
undeletable until some later download merged the location again.

Retry the transaction instead (up to three attempts, re-checking the merge
lease fence each time), and rethrow anything that isn't a lock conflict.

Observed as a flaky `cleanup-lifecycle` failure on the mysql CI jobs.
@LouisHaftmann
LouisHaftmann merged commit 0669de0 into dev Jul 29, 2026
24 checks passed
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.

ACTIONS_TOKEN_ISSUER incompatible with enterprise custom issuer

1 participant