Skip to content

Move authserver JWK parsing to go-jose v4 - #7012

Merged
jhrozek merged 2 commits into
mainfrom
go-jose-v4-migration
Nov 1, 2026
Merged

jhrozek merged 2 commits into
mainfrom
go-jose-v4-migration

Conversation

@jerm-dro

Copy link
Copy Markdown
Collaborator

Summary

go-jose v3 is affected by GHSA-mgcc-cww5-fp2q and has no patched release. This moves the auth server's JSON Web Key handling to go-jose v4.

  • Parse client JWKS (DCR requests, the configured-client JWKS file, and stored Redis clients) with go-jose v4. Keys are copied field by field into v3 types only where the OAuth library's interfaces require them (pkg/authserver/internal/fositejose).
  • Reject client_assertion values and authorization request objects whose JWS header embeds a jwk, or that are not compact-serialized.
  • Raise go-jose's RSA modulus parse limit to the server's existing 8192-bit maximum, so keys that were already accepted keep working.
  • Decode a stored client's JWKS separately in Redis, so a stored key set that no longer parses returns invalid_client rather than a storage error.

Type of change

  • Bug fix

Test plan

  • Unit tests (task test)
  • Linting (task lint-fix)

Does this introduce a user-facing change?

Yes.

  • Before: a client_assertion or request object with an embedded jwk header was accepted, and the embedded key was ignored.
  • After: it is rejected with invalid_client or invalid_request_object, and the rejection is logged at WARN. Clients that sign with their registered key are unaffected.

Special notes for reviewers

  • fosite workaround: fosite v0.49.0 still uses go-jose v3 in its interfaces and parses client assertions and request objects with it. Two pieces exist only because of that: the fositejose conversion package and the embedded-jwk guard (fosite_jws_guard.go). Both can be removed once fosite moves to go-jose v4, and comments in the code say so.
  • jose.MaxRSAModulusBits is a process-wide variable. Setting it to 8192 also applies to other go-jose v4 users in the same binary, whose default would otherwise be 4096.
  • The PR touches 13 non-test files, but most of them are one-line import swaps.

🤖 Generated with Claude Code

go-jose v3 validates RSA keys embedded in JWKs without bounding their
size (GHSA-mgcc-cww5-fp2q) and has no patched release. Parse keys with
go-jose v4.1.6 everywhere ToolHive owns the parsing, copying parsed keys
field by field into the v3 types fosite v0.49.0 still requires.

fosite itself parses client assertions and OIDC request objects with v3
before looking up a client, so refuse any such JWT whose header embeds
a jwk, or that is not a compact JWS, before fosite sees it. fosite never
uses an embedded key, so conforming clients are unaffected.

Raise go-jose's new 4096-bit RSA parse limit to our existing 8192-bit
policy so registered clients keep loading, and decode a stored Redis
key set separately so one that go-jose rejects still answers
invalid_client instead of failing the whole row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Malformed request objects receive different responses for existing back-channel-only clients and unknown clients, leaking client existence and violating the client-hiding contract.

2 open issues
What changed in this PR

Moves auth-server JWK parsing to go-jose v4 to address GHSA-mgcc-cww5-fp2q, while retaining compatibility with fosite’s v3 interfaces.

Changes:

  • Migrates client key parsing to v4 and adds field-by-field conversion for fosite.
  • Guards client assertions and authorization request objects against embedded keys and non-compact serialization.
  • Preserves the 8192-bit RSA limit and returns invalid_client for stored JWKS that cannot be parsed.
File Description
test/​e2e/​thv-operator/​virtualmcp/​virtualmcp_private_key_jwt_test.go Uses v4 keys in registration fixtures.
pkg/​oauthproto/​dcr.go Uses v4 JWK types for registration requests.
pkg/​authserver/​upstream/​oidc_test.go Migrates token-signing fixtures to v4.
pkg/​authserver/​storage/​redis.go Separates JWKS decoding from stored-client decoding.
pkg/​authserver/​storage/​redis_test.go Tests valid and invalid stored-JWKS round trips.
pkg/​authserver/​storage/​memory_test.go Converts v4 test keys for fosite clients.
pkg/​authserver/​storage/​configured_stamp_test.go Updates configured-client key fixtures.
pkg/​authserver/​storage/​configured_client_reconcile_test.go Uses v4 keys in reconciliation tests.
pkg/​authserver/​server/​registration/​dcr.go Converts fosite keys for v4 validation.
pkg/​authserver/​server/​registration/​dcr_test.go Migrates registration test keys to v4.
pkg/​authserver/​server/​registration/​configured_client.go Uses v4 configured-client key types.
pkg/​authserver/​server/​registration/​client.go Converts v4 keys when constructing fosite clients.
pkg/​authserver/​server/​registration/​client_test.go Adapts key assertions to the conversion boundary.
pkg/​authserver/​server/​provider.go Wraps client authentication with the JWS guard.
pkg/​authserver/​server/​handlers/​dcr.go Uses v4 keys in the registration handler.
pkg/​authserver/​server/​handlers/​dcr_test.go Updates registration-handler fixtures to v4.
pkg/​authserver/​server/​handlers/​authorize.go Rejects unsafe authorization request objects.
pkg/​authserver/​server/​handlers/​authorize_test.go Tests embedded-key rejection without redirects.
pkg/​authserver/​server/​fosite_jws_guard.go Adds checks before fosite parses untrusted JWS input.
pkg/​authserver/​server/​fosite_jws_guard_test.go Tests guard inputs and authentication delegation.
pkg/​authserver/​server/​crypto/​keys.go Sets v4’s RSA parsing limit to 8192 bits.
pkg/​authserver/​runner/​embeddedauthserver.go Loads configured JWKS files with v4.
pkg/​authserver/​runner/​embeddedauthserver_test.go Updates configured-JWKS fixtures to v4.
pkg/​authserver/​internal/​fositejose/​fositejose.go Adds field-by-field v3/v4 key conversion.
pkg/​authserver/​integration_test.go Migrates private-key JWT registration fixtures.
pkg/​authserver/​configured_client_runner_integration_test.go Writes configured-client JWKS using v4.
pkg/​authserver/​config.go Uses v4 keys in auth-server configuration.
pkg/​authserver/​config_test.go Updates configuration test keys to v4.
docs/​arch/​11-auth-server-storage.md Documents JWS restrictions, errors, and logging.

Review effort: Balanced

Comment thread pkg/authserver/server/crypto/keys.go Resolved
@jerm-dro
jerm-dro force-pushed the go-jose-v4-migration branch from 0a8d1ee to 6cc13a3 Compare October 30, 2026 17:19
@github-actions github-actions Bot added the size/M Medium PR: 300-599 lines changed label Oct 30, 2026
@codecov

codecov Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.01980% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.44%. Comparing base (20a3b96) to head (9c9c5e2).

Files with missing lines Patch % Lines
pkg/authserver/internal/fositejose/fositejose.go 96.87% 1 Missing ⚠️
pkg/authserver/server/handlers/authorize.go 92.85% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7012      +/-   ##
==========================================
+ Coverage   80.43%   80.44%   +0.01%     
==========================================
  Files         850      852       +2     
  Lines       86943    87035      +92     
==========================================
+ Hits        69932    70016      +84     
- Misses      17006    17014       +8     
  Partials        5        5              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Rejecting an unsafe request object after the back-channel-only client
check answered differently for configured and unknown clients, which
revealed whether a back-channel client exists. The check does not depend
on the client, so it now runs first.

Also pin that go-jose's RSA parse limit matches MaxRSAKeyBits, so client
keys above go-jose's 4096-bit default keep decoding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Oct 30, 2026

@jhrozek jhrozek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks good. I'll send a follow-up PR on top of it for a few things we found in review, including a case where the authorize request-object check can be skipped when the query string doesn't parse cleanly, and accepting padded base64url in JWKS the way go-jose v3 did.

@jhrozek
jhrozek merged commit a4aef63 into main Nov 1, 2026
51 checks passed
@jhrozek
jhrozek deleted the go-jose-v4-migration branch November 1, 2026 19:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Medium PR: 300-599 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants