I want to build an authentication service that uses JWT with signatures generated using RS256. In a single-server solution, the implementation is rather straightforward. However, when it comes to making it multi-node there are two related issues to which I'm not finding clear answers:
In a cluster solution, should every server use its own key pair or can a shared key pair be used by multiple nodes? In that case how to build efficiently a JWK that doesn't contain too many keys?
On the other hand, if sharing the private key is possible without incurring major security issues, which is the best approach to share the key and manage its rotation?
CodePudding user response:
If possible (IMO) you should use a single private key for signing access tokens.
Having multiple would just increase complexity (e.g. the back-end resource servers would need logic to work out what public key to use for JWT validation, or cycle through multiple public keys to validate a JWT) and I don't really see any security benefit.
FWIW there might be business reasons for needing to support multiple on the back-end e.g. if your company has multiple products with separate auth tenants/sign-on-domains you might need to support multiple keys in shared back-end services/capabilities - for this type of scenario you'd need to use something such as the token issuer (iss
) claim to determine what public key to use for JWT validation.
WRT rotating keys; if you're using a PaaS IDP like Auth0 it's all probably handled for you. If you're using a COTS product like Keycloak (or if you've rolled your own IDP) you'll need to have multiple public keys defined in your JWKS endpoint (albeit temporarily). The new key would be designated as 'active' and would be used for signing all subsequent tokens, whereas the old one would be designated as 'passive' and is only needed to verify access tokens that were issued prior to the rotation and are not yet expired. Note: once all access tokens signed using the old key are expired, you can probably decommission/remove the old key.
Here's a link to Keycloak documentation about rotating keys that might be useful.
In your back-end services where you do JWT validation, the public key corresponding to the key ID (kid
) claim on the JWTs should be cached. If a request is signed with the new private key the JWT should have a different key ID. If a back-end service encounters a new key ID, it should lazily go back to the JWKS endpoint to get the public key associated to the new key ID (and this should then be cached as well).
In most common programming languages there are libraries that handle most of this for you e.g. I've done JWT validation in .NET Core using the Microsoft.AspNetCore.Authentication package and all I needed to do was configure an Open ID matadata endpoint (which references the JWKS endpoint) and things like key rotating & caching was all handled transparently.