Skip to content

feat(agentex-ui): accept RSA (RS256) private_key_jwt keys#367

Merged
erichwoo-scale merged 1 commit into
mainfrom
erichwoo/agentex-ui-private-key-jwt-rsa
Jul 16, 2026
Merged

feat(agentex-ui): accept RSA (RS256) private_key_jwt keys#367
erichwoo-scale merged 1 commit into
mainfrom
erichwoo/agentex-ui-private-key-jwt-rsa

Conversation

@erichwoo-scale

@erichwoo-scale erichwoo-scale commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Brings agentex-ui's bespoke private_key_jwt signer back on par with @scale/oneauth-nextjs and, in the process, replaces the hand-rolled WebCrypto assertion code with jose. The signer was ES256/P-256 only; an app configured with an RSA OIDC_PRIVATE_KEY_JWK couldn't sign the client assertion. It now signs with EC (ES256/384/512) or RSA (RS256/384/512, incl. PS*), alg derived from the key.

Purely additive — the ES256 path is behaviorally unchanged.

What changed

Single file, agentex-ui/auth.ts (+ jose promoted to a direct dependency):

  • Sign with joseimportJWK + SignJWT replace base64url, importEs256Key, jwsSigningFor, and the manual JWT assembly (~55 lines → ~10). jose validates the JWK against its alg and handles every key type natively, so there are no silent-wrong-alg edge cases to guard by hand.
  • signingAlgForJwk derives the alg — explicit JWK alg, else EC by curve (ES256/384/512) / RSA → RS256 — for both the login-key import and the refresh assertion. RS384/RS512/PS256 are supported by declaring alg on the JWK.
  • jose was already in the tree transitively (it's what @auth/core uses); this makes it a direct dep.

Everything else was already at parity (agentex-ui set the original bar): getSessionToken BFF, token-stripped session, fail-loud misconfig guards, top-level-await key resolution, OIDC-discovery-driven endpoints.

Verification

tsc --noEmit, next lint (0 warnings), Prettier, and next build — all clean.

Test plan

  • Existing EC/P-256 OIDC_PRIVATE_KEY_JWK clients still log in and refresh (ES256, unchanged).
  • An RSA OIDC_PRIVATE_KEY_JWK (registered with private_key_jwt + matching public JWK) → login + refresh succeed (RS256 assertion accepted).
  • A JWK with alg: RS384/PS256 → assertion carries that alg.
  • A malformed / unsupported-kty OIDC_PRIVATE_KEY_JWK → clear error (jose rejects), not a silent wrong-alg.
  • client_secret_post path unaffected.

🤖 Generated with Claude Code

Greptile Summary

This PR adds RSA (RS256 and family) support to agentex-ui's private_key_jwt signer by replacing ~55 lines of hand-rolled WebCrypto JWT assembly with jose's importJWK + SignJWT. The ES256/P-256 path is preserved exactly; signingAlgForJwk adds EC-curve and RSA-key-type detection for the algorithm negotiation.

  • signingAlgForJwk respects an explicit alg field in the JWK, falls back to curve-based EC alg (ES256/384/512), or defaults to RS256 for bare RSA keys (PSS variants require alg to be declared on the JWK, which is documented in the PR).
  • importClientPrivateKey and the SignJWT flow replace importEs256Key, jwsSigningFor, and the manual base64url header/payload/signature construction — delegate alg validation and signing to jose entirely.
  • jose is promoted from a transitive dependency (via @auth/core) to a direct one at ^6.0.0.

Confidence Score: 5/5

Safe to merge — the change is purely additive, the EC/ES256 path is behaviorally unchanged, and the new RSA path delegates all alg validation to jose rather than adding bespoke string-matching that could be wrong.

The replaced code was simple enough that the rewrite is easy to audit line-by-line. signingAlgForJwk correctly checks jwk.alg first, EC curve second, and RSA last. The SignJWT builder covers iss/sub/aud/jti/iat/exp identically to the old manual assembly. No logic regressions visible.

No files require special attention — both changed files are straightforward.

Important Files Changed

Filename Overview
agentex-ui/auth.ts Replaces hand-rolled WebCrypto signer with jose's importJWK + SignJWT; adds signingAlgForJwk for EC/RSA alg detection; otherwise unchanged auth flow.
agentex-ui/package.json Adds jose ^6.0.0 as a direct dependency; it was already present transitively via @auth/core.

Reviews (3): Last reviewed commit: "feat(agentex-ui): accept EC/RSA private_..." | Re-trigger Greptile

@erichwoo-scale
erichwoo-scale requested a review from a team as a code owner July 16, 2026 21:28
Comment thread agentex-ui/auth.ts Outdated
@erichwoo-scale
erichwoo-scale force-pushed the erichwoo/agentex-ui-private-key-jwt-rsa branch from 59a5dc5 to ffee3f0 Compare July 16, 2026 21:47
The bespoke private_key_jwt signer was ES256/P-256 only. Sign the RFC 7523
client_assertion with `jose` (importJWK + SignJWT) instead of hand-rolled
WebCrypto, so an app can authenticate with an EC (ES256/384/512) or RSA
(RS256/384/512, incl. PS*) private JWK.

- Replaces base64url + importEs256Key + jwsSigningFor + the manual JWT assembly
  with jose, which validates the JWK against its alg and handles every key type
  natively (no silent-wrong-alg edge cases).
- signingAlgForJwk derives the alg (explicit JWK `alg`, else EC by curve / RSA
  → RS256) for both the login key import and the refresh assertion.
- jose is promoted from a transitive dep (via @auth/core) to a direct one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@erichwoo-scale
erichwoo-scale force-pushed the erichwoo/agentex-ui-private-key-jwt-rsa branch from ffee3f0 to 523e62e Compare July 16, 2026 21:58
@erichwoo-scale
erichwoo-scale enabled auto-merge (squash) July 16, 2026 21:58
@erichwoo-scale
erichwoo-scale merged commit e2c8a22 into main Jul 16, 2026
10 checks passed
@erichwoo-scale
erichwoo-scale deleted the erichwoo/agentex-ui-private-key-jwt-rsa branch July 16, 2026 21:59
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