Repository navigation
Conversation
Self-hosted deployments that supply only the JWT_JWKS env config could not sign storage URLs with an asymmetric key: urlSigningKey was auto-populated only in the multi-tenant JWKS loader, so single-tenant installs with an EC signing key silently fell back to the HMAC jwtSecret. - add isUrlSigningCapableJwk / pickUrlSigningKey helpers in config.ts (oct with k or EC with d; reject use="enc" per RFC 7517 §4.2) - parse JWT_JWKS with auto-populated urlSigningKey when the field is absent, preserving an explicit value when provided - mergeTenantJwksWithLegacyKeys now preserves the tenant urlSigningKey with a fallback picked from the merged key list - refactor jwksManager tenant loader to use the shared capability check - document the URL signing contract in .env.sample - 9 unit tests covering selection, exclusion, and backward compatibility HS256 default deployments (no JWT_JWKS) are unaffected.
…supabase#629) Adds a unit-level round-trip test that provisions JWT_JWKS with only an ES256 EC key, lets the config parser auto-populate urlSigningKey, calls the real signJWT, and verifies the token header reports alg=ES256 with the expected kid — proving asymmetric signing actually happens rather than silently falling back to the HS256 jwtSecret. Also adds a negative control that an RSA-only JWT_JWKS still leaves urlSigningKey unset (RSA cannot sign storage URLs per the maintainer whitelist) and that the keys are still parsed for verification.
Operators who provisioned JWT_JWKS without a URL-signing-capable key previously had storage URL signing silently fall back to the HMAC jwtSecret, with the only observable symptom being the signed-URL header bytes. The github issue reporter spent debugging time tracing this to the config. Now the first call that reads the single-tenant JWT config emits a one-shot startup warning describing the mismatch. - describeJwtJwksMisconfiguration helper in config.ts (pure, no logger dependency so it stays importable from the module graph that logger itself depends on) - getSingleTenantJwtConfig emits the warning via logSchema.warning once per process, keeping the fast path allocation-free afterwards - warning metadata exposes only key count, kty list, and configured urlSigningJwkType — no key material, no HMAC secret - 5 unit tests pinning the dangerous shape and the four excluded silent-safe scenarios, with an explicit red-team guard that the serialized description never carries key material HS256 default deployments and well-configured JWT_JWKS tenants stay silent.
…pabase#629) The two capability helpers are consumed by three independent code paths (JWT_JWKS env parser, tenant JWKS merge, per-tenant jwksManager loader). Previously they were only reached indirectly through the config-parsing and misconfiguration-detection tests. Add dedicated table-driven tests that pin every branch of the capability predicate (oct/EC with and without private material, RSA, OKP, use="enc" for both key types) and the ordering/empty-list semantics of the picker, so a future refactor that touches these helpers surfaces a direct failure instead of a confusing cross-module regression.
This was referenced Oct 7, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Closes #629.
Self-hosted deployments that supply only the
JWT_JWKSenv config could not sign storage URLs with an asymmetric key:urlSigningKeywas auto-populated only in the multi-tenant JWKS loader, so single-tenant installs with an EC signing key silently fell back to the HMACjwtSecret. The reporter spent debugging time tracing a "storage outage" to this.This PR makes the single-tenant env parser mirror the per-tenant selection logic that already lives in
jwksManager, and adds a one-shot startup warning for the mis-configuration shape the reporter actually hit.Changes
src/config.ts— new exported helpersisUrlSigningCapableJwk+pickUrlSigningKey+describeJwtJwksMisconfiguration.JWT_JWKSparsing now auto-populatesurlSigningKeywith the first signing-capable JWK (octwithkorECwithd, skippinguse: "enc"per RFC 7517 §4.2), preserving an explicit value when supplied.src/internal/auth/jwks/manager.ts— tenant JWKS loader now uses the shared capability check (DRY, behavior unchanged).src/internal/database/tenant.ts—mergeTenantJwksWithLegacyKeysnow preserves the tenant-provisionedurlSigningKeywith a fallback picked from the merged key list.getSingleTenantJwtConfigemits a one-shotlogSchema.warningwhenJWT_JWKShas keys but none can sign URLs, logging only key count +ktylist + configuredurlSigningJwkType(no key material, no HMAC secret)..env.sample— documented the URL-signing contract.src/config.test.ts— 25 unit tests across four groups: direct capability-helper tests, config auto-selection, mis-configuration detection, and ajoseround-trip proving the auto-selected EC key produces anES256token header instead ofHS256.RSA and OKP keys stay verification-only — this matches the existing
UrlSigningJwksConfigKey = oct | ECandURL_SIGNING_JWK_TYPES = ['HS512', 'ES256']whitelist. HS256 default deployments (noJWT_JWKS) are unaffected.Backward compatibility
JWT_JWKS→ unchangedJWT_JWKScontaining an expliciturlSigningKey→ unchanged (explicit value is preserved)JWT_JWKScontaining only verification-shaped keys → unchanged behavior (HMAC fallback) + one new startup warningJWT_JWKScontaining a signing-capableoctorECkey → URLs are now actually signed with that key instead of the HMACjwtSecret(the behavior the issue reporter expected)Test plan
npm run test:unit -- src/config.test.ts— 25 new tests passsrc/config.test.ts src/internal/auth src/http/plugins/signature-v4.test.ts→ 174/175 pass (one pre-existing env-leakage unrelated to this change)alg: "ES256"in the signed token headern,e,k,d, orkidnpx tsc --noEmitclean on the changed files (one unrelated pre-existing error insrc/internal/cluster/eks.ts)