micronaut-security icon indicating copy to clipboard operation
micronaut-security copied to clipboard

JWTClaimsSetGenerator#generateClaims(Authentication authentication, @Nullable Integer expiration) overrides provided expiration with original exp claim

Open pmossman opened this issue 7 months ago • 0 comments

Expected Behavior

I want to generate a new JWT with the current Authentication, but with a new expiration.

Calling JwtTokenGenerator.generateToken(authentication, newExp) should create a new token derived from the authentication, with an exp claim that matches thenewExp that I pass in.

Actual Behaviour

Instead, I get a new token with an exp claim that matches the previous exp claim from the authentication.

It looks like the JWTClaimsSetGenerator sets the exp claim from the provided expiration value, but then overwrites all claims from the authentication.

Instead, if an expiration is provided, it should be set after populating claims from the authentication.

Here's the relevant code as it looks today:

  public Map<String, Object> generateClaims(Authentication authentication, @Nullable Integer expiration) {
    JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
    this.populateIat(builder);
    this.populateExp(builder, expiration); // sets the 'exp' claim with provided expiration value
    this.populateJti(builder);
    this.populateIss(builder);
    this.populateAud(builder);
    this.populateNbf(builder);
    this.populateWithAuthentication(builder, authentication); // overwrites the 'exp' claim with the authentication's original exp
    if (LOG.isDebugEnabled()) {
      LOG.debug("Generated claim set: {}", builder.build().toJSONObject());
    }

    return builder.build().getClaims();
  }

Steps To Reproduce

Inject a JwtTokenGenerator and attempt to generate a new token with a new desired expiration:

@Singleton
class NewTokenHandler(
  private val jwtTokenGenerator: JwtTokenGenerator,
  private val securityService: SecurityService,
) {
  fun getNewToken(): String {
    val authentication = securityService.authentication.orElseThrow()

    return jwtTokenGenerator.generateToken(authentication, Duration.ofHours(48).toSeconds().toInt()).orElseThrow()
  }
}

Inspect the new token and notice that the exp claim does not change, regardless of what the specified expiration is set to.

Environment Information

openjdk 21.0.3 2024-04-16 LTS
OpenJDK Runtime Environment Corretto-21.0.3.9.1 (build 21.0.3+9-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.3.9.1 (build 21.0.3+9-LTS, mixed mode, sharing)

Example Application

No response

Version

4.5.0

pmossman avatar Jul 05 '24 17:07 pmossman