Skip to content

feat: verify JWTs against authn's JWKS instead of a shared signing key - #146

Merged
braghettos merged 6 commits into
mainfrom
feat/rs256-jwks
Aug 13, 2026
Merged

feat: verify JWTs against authn's JWKS instead of a shared signing key#146
braghettos merged 6 commits into
mainfrom
feat/rs256-jwks

Conversation

@EdmondDantes21

Copy link
Copy Markdown
Contributor

Summary

  • snowplow no longer holds a copy of authn's signing key. It resolves RS256 verification keys by kid from authn's JWKS endpoint (GET /.well-known/jwks.json) via a cached jwtutil.JWKSKeySource, defaulting to <URL_AUTHN>/.well-known/jwks.json unless --jwks-url/JWT_JWKS_URL overrides it.
  • Fetches are lazy (first validation, not at boot) and cached — snowplow starts and serves unauthenticated routes even if authn isn't up yet, and a key rotation on authn's side needs no snowplow redeploy.
  • middleware.UserConfig/middleware.RefreshAuth take a jwtutil.KeySource in place of a raw signing-key string. The snowplow-local UserConfig mirror of plumbing's use.UserConfig (the AC-D3.14 provenance guard, TestUserConfigMirror_PlumbingVersionPin) is re-audited line-by-line against upstream's new KeySource-based flow and re-pinned.
  • Chart: jwtSignKeySecretName and its envFrom secretRef are gone; jwt.{jwksUrl,cacheTTL,minRefreshInterval,requestTimeout} configures the JWKS client instead. No Secret is mounted on snowplow anymore.

Cross-repo dependency

Depends on plumbing PR krateo-platformops/plumbing#21 and pairs with authn PR krateo-platformops/authn#20 (which adds the JWKS endpoint this reads from).

go.mod is pinned to the plumbing PR's branch-tip commit as a pseudo-version so this branch builds and tests green today; internal/handlers/middleware/userconfig.go's PinnedPlumbingVersion is pinned to the same pseudo-version. Before merging this PR, once plumbing is tagged, push a follow-up commit here: go get github.com/krateo-platformops/plumbing@<tag> && go mod tidy, then update PinnedPlumbingVersion to match (no further re-audit needed — content is identical, just the version string).

Test plan

  • go build ./... / go test ./... (go/snowplow), including TestUserConfigMirror_PlumbingVersionPin, green against the plumbing pseudo-version
  • Re-verify green after the go.mod follow-up once plumbing is tagged

Pinned to the krateo-platformops/plumbing@feat/rs256-jwks-keysource commit
pending release; re-pin to the tagged version once
krateo-platformops/plumbing#21 is merged and tagged.
snowplow no longer holds a copy of authn's signing key. It resolves RS256
verification keys by "kid" from authn's JWKS endpoint
(GET /.well-known/jwks.json) via a cached jwtutil.JWKSKeySource, defaulting to
<url-authn>/.well-known/jwks.json unless --jwks-url/JWT_JWKS_URL overrides it.
Fetches are lazy (first validation, not at boot) and cached, so snowplow
starts and serves unauthenticated routes even if authn isn't up yet, and a
key rotation on authn's side needs no snowplow redeploy.

middleware.UserConfig and middleware.RefreshAuth take a jwtutil.KeySource in
place of the raw signing-key string; the snowplow-local UserConfig mirror of
plumbing's use.UserConfig (AC-D3.14 provenance guard) is re-audited
line-by-line against upstream's new KeySource-based flow and re-pinned.

Chart: helm/snowplow's jwtSignKeySecretName and its envFrom secretRef are
gone; jwt.{jwksUrl,cacheTTL,minRefreshInterval,requestTimeout} configures the
JWKS client instead. No Secret is mounted.
braghettos and others added 4 commits August 13, 2026 19:31
…cret

The RS256 migration made plumbing's e2e.SignUp expect a PEM-encoded RSA private
key (JWTSignKey) + a JWTKeyID, but six test setups still passed the old symmetric
secret "abbracadabbra", so they failed with
  e2e.go:50: invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key
in TestResolveAPI, TestResolveWidgets(_Extras), TestGet, TestRESTAction, TestCallHandler.

Generate an inline PKCS1 PEM RSA key per test (matching the already-fixed
internal/rbac/rbac_test.go) and pass JWTSignKey + JWTKeyID: "test-kid".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJsLqtryCgWwEt8FnPE1se
@braghettos
braghettos merged commit 18a5413 into main Aug 13, 2026
15 of 16 checks passed
@braghettos
braghettos deleted the feat/rs256-jwks branch August 13, 2026 18:41
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.

2 participants