Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT builder code and tests #158

Merged
merged 1 commit into from
Jan 13, 2020
Merged

JWT builder code and tests #158

merged 1 commit into from
Jan 13, 2020

Conversation

sberyozkin
Copy link
Contributor

@sberyozkin sberyozkin commented Dec 19, 2019

Fixes #137

I've spent a lot of time on this iteration round trying to address all the JWT protection scenarios without over-complicating things while also providing the optimized support for the mainstream sign JWT path. The following is now supported at the API level:

  1. Sign the claims with the keys created in the code or loaded from the configured path.
  2. Same as 1) but also with an option to customize the Json Web Signature headers
  3. Encrypt the claims with the keys created in the code or loaded from the configured path.
  4. Same as 3) but also with an option to customize the the Json Web Encryption headers
  5. Sign the claims as inner JWT and encrypt with the keys created in the code or loaded from the configured path (for the sign/encrypt)

Sign the claims without setting the headers

Jwt
  .claims()
      .issuer("https://server.com")
      .claim("customClaim", 3)
   .sign();   // or .sign(createPrivateKey()) or .sign(createSecretKey())

or

Jwt
   .claims()
       .claim("custom-array1", Arrays.asList("1", "2"))
       .claim("custom-array2", Json.createArrayBuilder().add("1").add("2").build())
       .claim("mapClaim", Collections.singletonMap("key", "value"))
       .claim("mapClaim2", Json.createObjectBuilder().add("jsonKey", "jsonValue").build())
    .sign();  // or .sign(createPrivateKey()) or .sign(createSecretKey())

or

Jwt .claims(Collections.singletonMap("customClaim", "custom-value"))
   .sign();   // or .sign(createPrivateKey()) or .sign(createSecretKey())

or

Jwt.claims("/testJsonToken.json")
     .sign();  // or .sign(createPrivateKey()) or .sign(createSecretKey())

Set the headers and sign the claims

// Create JWT JSON Web Signature builder 
JwtSignatureBuilder jwtSignatureBuilder = Jwt.claims("/testJsonToken.json").jws();
jwtSignatureBuilder
     .signatureKeyId("some-key-id")
     .seignatureAlgorithm(SignatureAlgorithm.ES256) 
     .header("custom-header", "custom-value");    
     .sign();  // or .sign(createPrivateKey())```

Encrypt the claims

Jwt.claims("/testJsonToken.json")
     .encrypt();  // or .encrypt(createPublicKey()) or .encrypt(createSecretKey())

or

JwtEncryptionBuilder jwtEncryptionBuilder = Jwt.claims("/testJsonToken.json").jwe();
jwtEncryptionBuilder
     .keyEncryptionKeyId("some-key-id")
      .keyEncryptionAlgorithm(KeyEncryptionAlgorithm.ECDH_ES_A256KW)
     .header("custom-header", "custom-value");
     .encrypt();  // or .encrypt(createPublicKey())

Sign the claims and encrypt the inner JWT

Jwt.claims("/testJsonToken.json")
  .innerSign()  // or .innerSign(createPrivateKey()) or .innerSign(createSecretKey())
  .encrypt();  // or .encrypt(createPublicKey()) or .encrypt(createSecretKey())

or

JwtEncryptionBuilder jwtEncryptionBuilder = Jwt.claims("/testJsonToken.json")
     .jws()
        .signaturekeyId("sign-key-id")
        .innerSign();   // or .innerSign(createPrivateKey()) or .innerSign(createSecretKey())

jwtEncryptionBuilder
     .keyEncryptionKeyId("some-key-id") 
     .header("custom-header", "custom-value");
     .encrypt();  // or .encrypt(createPublicKey()) or .encrypt(createSecretKey())

The locations and types of the configured keys
Sign and encrypt methods which take no parameters (sign(), innerSign() and encrypt()) depend on the keys being loaded from the configured locations. The same rules which are used to load the MP JWT verification keys apply here: the keys can be PEM, JWKor JWK in the JWK set and they can be located on the classpath, filesystem, or HTTP(S) URLs

API Design Considerations

  • Should be possible to create JWT with the simple and complex claims, created in the code or loaded from the external JSON resources
  • No mix in of setting the claims and the signature or encryption headers at the same level in order to have a clean JWT claims centric API
  • Since setting the headers is technically part of Json Web Signature or Json Web Encryption sequence creation process, setting the headers requires either jws() or jwe() transitions, with the extra benefit being that the users will see JWT as an instance of JWS (sign) and/or JWE (encrypt, sign-encrypt)
  • Should be possible to create the keys in the code or from the configuration (the idea behind the latter was proposed by @FroMage )
  • Sign, encrypt and sign/encrypt flows should be easy to follow
  • SignatureAlgorithm, KeyEncryptionAlgorithm and ContentEncryptionAlgorithm values are currenty restricted but can be opened up depending on the requirements. Note these enums are not part of the build subpackage because I expect we will use the same algorithm values independently, as part of the token verification process..

CC @starksm64 @kenfinnigan @darranl

@sberyozkin
Copy link
Contributor Author

I've added the encryption tests. Will be spending the next round on cleaning things up and then will open this PR for review

@sberyozkin sberyozkin marked this pull request as ready for review December 27, 2019 12:18
@FroMage
Copy link

FroMage commented Jan 6, 2020

This looks good, but we'll need to build the following Quarkus web scenarios to validate the design:

  • REST endpoint /login with BASIC auth, creates and returns a JWT claim for the user
  • REST endpoint /protected uses that claim to verify it
  • test uses /login and /protected with the returned claim
  • test creates its own claim and calls /protected with it

But I'm not sure I'll have time soon…

@sberyozkin
Copy link
Contributor Author

@FroMage thanks, I don't see any problems with those cases as far as this design is concerned. of course we can have things improved going forward, I'd like to make it available to Quarkus users asap and then keep updating the API if needed

@starksm64 starksm64 merged commit 37a1687 into smallrye:master Jan 13, 2020
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.

Provide a simple JWT generation utility code
3 participants