Consider JWT minting component

Issue #395 open
Josh Cummings created an issue

Nimbus has a robust component for processing a serialized JWT.

It would be nice to have a comparable component for minting a new JWT.

I’m thinking that the contract might look something like:

public String mint(JWSHeader header, JWTClaimsSet claimsSet, SecurityContext context)

The key would be retrieved from a JWKSource using the JWSHeader for selecting the JWK. This could be overridden by setting a custom JWKSource.

Having this action bundled together in a component makes it a bit easier for frameworks like Spring Security to expose configuration hooks.

Selecting the JWK based on the provided header makes sense to me, though perhaps there are other ways that folks are doing it.

I’ll submit a PR that performs the above, though I wonder if an additional method (JWK, JWSHeader, JWTClaimSet, SecurityContext) should be added. An application can achieve the same outcome by passing a JWKSecurityContext and configuring with a JWKSecurityContextJWKSet, but it feels a little cumbersome that way.

Comments (6)

  1. Vladimir Dzhuvinov
    • changed status to open

    Thanks Josh, this topic has not been given much attention yet. I mean, providing a general, universal and fairly robust mini framework for minting JWTs and general JOSE objects.

    In 2020 Justin Richer contributed code to help with JWS minting, but that was for a rather specific case: https://www.javadoc.io/doc/com.nimbusds/nimbus-jose-jwt/latest/com/nimbusds/jose/produce/JWSSignerFactory.html

    I'm currently reviewing some Connect2id server code and will use the opportunity to see if the suggested contract could work here. My strategy over the years has been to try to identify truly reusable code and patterns, and move them into the OIDC / OAuth SDK or here.

    We'll also study the PR https://bitbucket.org/connect2id/nimbus-jose-jwt/pull-requests/73 that you submitted.

    Thanks!

    Vladimir

  2. Vladimir Dzhuvinov

    On first review, it looks like the proposed component is going to work here.

    One bit that we found missing is the ability to configure the JWSMinter to include a X.509 certificate chain (x5c) in the JWS headers (when the signing key is selected).

    Here is the complete list of key specific JWS headers:

  3. Josh Cummings reporter

    Good point, JWKMatcher#forJWSHeader seems like a good place to enhance that. I can add that to my PR if you like, or would it be better to hammer that out elsewhere?

  4. Vladimir Dzhuvinov

    Hi Josh,

    I meant a config to determine what key specific JWS headers to set in

    https://bitbucket.org/connect2id/nimbus-jose-jwt/pull-requests/73#Lsrc/main/java/com/nimbusds/jwt/mint/DefaultJWSMinter.javaT59

    JWSHeader withJwk = new JWSHeader.Builder(header)
      .keyID(jwk.getKeyID())
      .build();
    

    after the JWS key is selected. The default impl in the PR sets the “kid”, but “x5c” and “x5u” can also come up. Covering all listed key specific headers would be ideal.

    The JWK matching itself looks fine. The assumption (from reading the code) is that the client code needs to supply a minimal JWS header (setting the “alg” and perhaps “typ”) and let the minter fill in the key related headers.

    I’m also curious if the use cases you’ve dealt with include some form of key rotation / key rollover. In the c2id server there is a convention to insert new JWKs at the top of the JWK set, so the proposed code will work for us. But there might be other conventions. My suggestion is to document this (for now) so that people are aware which key is going to get used if multiple match.

  5. Josh Cummings reporter

    Got it. I’ve updated the PR to add the x5u, x5c, x5t, and x5t#s256 headers. It pulls them from the JWK returned from the lookup, same as the kid. The JavaDoc is updated, too.

    Also, I think it will be helpful for callers to be able to inspect the headers that were added without deserializing, so I changed the return type to SignedJWT. Callers would do minter.mint(headers, claims, null).serialize() instead.

    Yes, supporting rotation is an important use case, and I think that selecting the first in the list is a reasonable default. I’ve added a test to demonstrate how that can be configured using the existing API. Personally, I’d prefer allowing for a custom JWKSource over introducing a new interface that returns a single JWK.

    I was thinking about use cases where the returned JWK doesn’t have any identifiers, e.g. no kid, etc. In that case, the signed JWT would have no hints for the recipient to know how to verify the signature. Since the spec only indicates that a JWS SHOULD have key identifying information in the header (as opposed to MUST), I’m inclined to leave it as is. If that sounds like an issue, though, maybe the implementation could throw an exception in that circumstance.

  6. Log in to comment