Support RS256/JWKS signed JSON Web Token for SSO login - #613
Open
gilfixefy wants to merge 2 commits into
Open
Conversation
The value of config key "sso.subject" was assigned to the "audience" verify option instead of "subject". A configured subject therefore silently replaced the audience check instead of validating the "sub" claim of the SSO token.
External SSO tokens could only be validated with a shared secret so far, which
does not work with identity providers signing their tokens with a private key.
Beside "sso.jwtSharedSecret" the key to validate the signature can now be set as
PEM encoded public key ("sso.jwtPublicKey", either the key itself or the name of
a file containing it) or downloaded from the JSON Web Key Set endpoint of the
identity provider ("sso.jwksUri"). Downloaded keys are cached in memory and
refreshed after "sso.jwksCacheMaxAge" seconds as well as on unknown key ids,
the latter rate limited to not flood the identity provider with requests.
No additional npm dependency is needed, the keys are fetched and imported with
the node core modules https and crypto.
Exactly one of the three key sources must be configured. HMAC algorithms are
rejected on startup whenever a public key is used - the public key is known to
everybody and could be used as shared secret to sign forged tokens otherwise.
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.
Summary
External SSO tokens (
/ssoroute) can currently only be validated with a shared secret, so identityproviders signing their tokens with a private key (Keycloak, Auth0, Entra ID, Google, Okta, ...) cannot
be used. This PR adds validation of tokens signed with asymmetric algorithms (RS256, PS256, ES256, ...).
Beside the existing
sso.jwtSharedSecretthe key to validate the signature with can now be configured as:sso.jwtPublicKey(envSSO_JWT_PUBLIC_KEY) - PEM encoded public key or the name of a file containingit, so it can be mounted as Docker/Kubernetes secret and referenced by name
sso.jwksUri(envSSO_JWKS_URI) - url of the JSON Web Key Set endpoint of the identity providerExactly one of the three key sources must be set, everything else is rejected on startup.
Additional config keys:
sso.jwksCacheMaxAge(envSSO_JWKS_CACHE_MAX_AGE, default 600 seconds) and anenv var for the already existing algorithm list (
SSO_JWT_ALGORITHMS, comma separated).No new dependency
The keys are downloaded with the node core
httpsmodule and imported withcrypto.createPublicKey(),therefore no additional npm package (like
jwks-rsaand its transitive dependencies) is pulled into theimage. All new code lives in
lib/ssoKeys.js,lib/app.jsonly passes the key resolver tojwt.verify().Keys downloaded from the JWKS endpoint are cached in memory, refreshed after
sso.jwksCacheMaxAgesecondsand whenever a token references an unknown key id. The latter is rate limited to one download every 30
seconds so unknown key ids cannot be used to flood the identity provider. If the endpoint is temporarily
unreachable the keys downloaded before are used as long as possible.
Security
HMAC algorithms are rejected on startup whenever a public key or a JWKS endpoint is configured - a public
key is known to everybody and could be used as shared secret to sign forged tokens otherwise.
octkeys(shared secrets) published at a JWKS endpoint are ignored for the same reason, as are keys marked for
encryption only.
Included bugfix (separate commit)
sso.subjectwas assigned to theaudienceverify option instead ofsubject, so a configured subjectsilently replaced the audience check instead of validating the
subclaim. Kept as its own commit to beable to drop or cherry-pick it independently.
Test plan
npm test- 30 new tests intest/testSsoKeys.jscovering key loading from PEM string and file,JWKS download, caching, refresh, rate limiting for unknown key ids, fallback to cached keys on endpoint
errors, malformed key sets, RS256 end-to-end verification and the new config validation rules
with another key is rejected ("invalid signature"), replayed token hits the existing single-use check,
JWKS endpoint is called only once due to caching
sso.jwtPublicKeypointing to a key file, including subject mismatch being rejectedredis-commander --testfor the new validation errors and for the unchanged shared secret setupRemarks
enginesfield still says>=12and was leftuntouched - happy to bump it if wanted
envlist