Skip to content

docs(guides): add GitHub Actions JWT bearer token guide - #2346

Merged
ssveta7ak merged 9 commits into
mainfrom
sveta/docs-jwt-access
Aug 13, 2026
Merged

docs(guides): add GitHub Actions JWT bearer token guide#2346
ssveta7ak merged 9 commits into
mainfrom
sveta/docs-jwt-access

Conversation

@ssveta7ak

Copy link
Copy Markdown
Contributor

Summary

Adds docs/guides/github-actions, a guide for letting a GitHub Actions workflow call a Pomerium-protected route using the OIDC token GitHub issues to every job, with no stored credentials.

The route uses bearer_token_format: jwt and trusts https://token.actions.githubusercontent.com through an identity_providers entry; the workflow requests a token for the route's audience and presents it as a bearer token. Authorization is entirely policy-driven on the token's claims, and the guide is explicit that any GitHub repository can mint a token from this issuer, so pinning claim/repository is the whole authorization boundary.

This is stacked on #2279 — it links to the identity_providers, per-route identity_providers, and bearer-token-access pages introduced there, so it cannot build against main until that merges.

Not yet released. The feature landed in pomerium/pomerium@69d7f68f (2026-08-10), after v0.32.5, so no tagged release supports this guide. Worth holding publication until a release includes it.

Verified against a live deployment rather than written from the reference alone: the guide's workflow, copied verbatim, returns Pomerium answered: HTTP 200, and Pomerium logs allow: true with allow-why-true: ["claim-ok"] and the identity github-actions/repo:.... Both the push and workflow_dispatch triggers were exercised.

An independent review pass against the Pomerium source and GitHub's OIDC documentation caught two errors that are fixed here:

  • curl --fail only fails on status 400 and above, so a 302 sign-in redirect would have passed the workflow step silently. The call now also sends Accept: application/json so denials arrive as 401/403.
  • The JWKS fetch is lazy (on first verification), not at config load; the diagram said otherwise.

The guide also avoids pinning the sub claim, because repositories created after 2026-07-15 get the immutable format embedding numeric owner and repository IDs — confirmed in the live token captured during testing.

Related

AI disclosure

Claude Code. The guide was AI-drafted, then fact-checked by a separate adversarial review pass against the Pomerium source and GitHub's current OIDC documentation, which produced the two corrections listed above. Every configuration snippet, status code, and log line was verified against a live Pomerium deployment before submission. Reviewed and edited by me.

Checklist

  • reference any related issues
  • updated docs
  • updated UPGRADING.md — n/a, new guide
  • updated CHANGELOG.md — n/a, new guide
  • disclosed AI usage (or wrote "none") per AI_POLICY.md

wasaga and others added 6 commits July 13, 2026 18:26
…okens & JWTs

Add a cohesive capabilities page introducing how clients present an
Authorization: Bearer token to Pomerium to reach upstreams without an
interactive sign-in, covering all four bearer_token_format modes
(default, idp_access_token, idp_identity_token, jwt) as a single feature.

- New capabilities page with Kubernetes service account example
- New reference pages for jwt_allowed_issuers and jwt_allowed_audiences
- Document the new jwt option on the bearer-token-format reference
- Register the capabilities page in the sidebar
Reduce emphasis to structural labels, thin em-dashes, de-bold inline
HTTP status codes, and recast the two parallel no-X-no-Y-no-Z fragments.
Prose-only; no content or link changes.
The JWT bearer-token feature shipped with a consolidated identity_providers
map rather than the flat jwt_allowed_issuers + jwt_allowed_audiences settings
these pages originally documented. Rewrite the pages to match:

- identity_providers is a map keyed by provider name; audiences are
  per-provider (required, non-empty, fail-closed). Drop the old 'name' field.
- Routes select providers via an optional per-route identity_providers
  allowlist; bearer_token_format: jwt remains the gate.
- Document the identity model: session idp_id = provider name, user id =
  <provider-name>/<sub>, sub required, session TTL capped to
  min(token exp, cookie_expire), raw JWT not persisted.
- supported_algs rejects none/HS*; JWKS TLS reuses the global
  certificate_authority_file.

Replace reference/jwt-allowed-issuers with reference/identity-providers
("JWT Identity Providers"), delete reference/jwt-allowed-audiences, and add
reference/routes/identity-providers for the per-route allowlist.
The capabilities page split format coverage: it pointed to Bearer Token
Format for the IdP formats while covering the jwt format itself, a false
symmetry since the reference is the setting's home for all four formats.

Move the format-agnostic mechanics and the four-format table to the
Bearer Token Format reference, and the caller-identity behavior to JWT
Identity Providers. The capabilities page becomes a pure jwt/M2M workflow
guide that references those, collapsing duplication that already existed
between it and the identity_providers reference.
Add a guide for letting a GitHub Actions workflow call a Pomerium-protected
route with the OIDC token GitHub issues to each job, verified against the
trusted issuer declared in identity_providers on a route using
bearer_token_format: jwt, and authorized on the token's claims.

AI usage disclosure (AI_POLICY.md): drafted and revised with Claude Code.
The workflow, config, status codes, and log output were verified against a
live Pomerium deployment and the Pomerium source before publishing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ssveta7ak
ssveta7ak requested a review from a team as a code owner August 12, 2026 15:48
@ssveta7ak
ssveta7ak requested review from kenjenkins and removed request for a team August 12, 2026 15:48
@netlify

netlify Bot commented Aug 12, 2026

Copy link
Copy Markdown

Deploy Preview for pomerium-docs ready!

Name Link
🔨 Latest commit 2be9b21
🔍 Latest deploy log https://app.netlify.com/projects/pomerium-docs/deploys/6a7dc88bfce88f0008f6aa20
😎 Deploy Preview https://deploy-preview-2346--pomerium-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@ssveta7ak
ssveta7ak requested a review from desimone August 12, 2026 15:49
@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown

Greptile Summary

Adds documentation for authenticating GitHub Actions and other machine workloads to Pomerium-protected routes using verified JWT bearer tokens.

  • Adds a GitHub Actions OIDC workflow guide with claim-based repository and branch authorization.
  • Documents JWT identity providers, per-route provider allowlists, bearer-token behavior, and Kubernetes service-account usage.
  • Registers the bearer-token capability in the documentation sidebar and records why automated example validation is skipped.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
content/docs/guides/github-actions.mdx Adds the end-to-end GitHub Actions OIDC configuration, workflow, verification steps, troubleshooting, and security guidance.
content/docs/capabilities/bearer-token-access.mdx Adds an overview of JWT bearer authentication with Kubernetes, SPIFFE, and GitHub Actions examples.
content/docs/reference/identity-providers.mdx Documents global JWT identity-provider configuration, verification behavior, session derivation, and platform-specific syntax.
content/docs/reference/routes/identity-providers.mdx Documents the per-route identity-provider allowlist for JWT bearer-token routes.
content/docs/reference/bearer-token-format.mdx Extends the bearer-token reference with the JWT format and explains extraction, verification, session creation, and authorization.
sidebars.js Adds the bearer-token capability page to the primary documentation sidebar.

Sequence Diagram

sequenceDiagram
  participant W as GitHub Actions workflow
  participant G as GitHub OIDC provider
  participant P as Pomerium
  participant U as Protected upstream
  W->>G: Request JWT for route audience
  G-->>W: Short-lived signed JWT
  W->>P: Request with Bearer JWT
  P->>G: Fetch and cache signing keys
  P->>P: Verify issuer, signature, expiry, and audience
  P->>P: Authorize repository and optional ref claims
  P->>U: Proxy authorized request
  U-->>W: Response
Loading

Fix All in Greploop

Reviews (4): Last reviewed commit: "docs(guides): mark github-actions guide ..." | Re-trigger Greptile

Base automatically changed from wasaga/docs-jwt-access to main August 12, 2026 17:36
kenjenkins and others added 2 commits August 13, 2026 11:08
revisions to github actions guide page

Reorder and streamline the introduction, reduce some duplication, and
remove some details that might be more confusing than helpful.
…dation (#2346)

The fixture harness seals a guide into Docker Compose with an in-network
Keycloak. Neither half of this flow survives that: a GitHub Actions OIDC
token can only be minted inside a runner, where GitHub injects the
ACTIONS_ID_TOKEN_REQUEST_* variables, and Pomerium must reach
token.actions.githubusercontent.com for the issuer's JWKS.

Add validate/SKIP with that reason and the manual validation record, plus
validate/screenshots-skip, which the media policy requires because the
guide references no screenshot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ssveta7ak
ssveta7ak merged commit 68f3c51 into main Aug 13, 2026
13 checks passed
@ssveta7ak
ssveta7ak deleted the sveta/docs-jwt-access branch August 13, 2026 19:06
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.

3 participants