Home > Net >  Custom JWT expiration in micronaut security
Custom JWT expiration in micronaut security

Time:04-22

The expiration time of a JWT can be set by configuring micronaut.security.token.jwt.generator.access-token-expiration.

Is it possible to have a custom value for individually issued JWT tokens? Searching the documentation I haven't found any useful information except you can replace the BearerTokenRenderer and return a custom response.

    public AccessRefreshToken render(Integer expiresIn, String accessToken, @Nullable String refreshToken) {
    return new AccessRefreshToken(accessToken, refreshToken, BEARER_TOKEN_TYPE, ***customValue**);
}

Will returning a different value for expires_in work in this case?

CodePudding user response:

According to micronaut documentation - pt 9.3.5

You can replace ClaimGenerator with your own: ClaimGenerator

Which has a method with parameter Integer expiration

You can replace classes by using @Replaces annotation like described here:

https://micronaut-projects.github.io/micronaut-core/latest/guide/#replaces

I hope that solves your issue

CodePudding user response:

The solution is to extend the JwTClaimsSetGenerator

@Replaces(JWTClaimsSetGenerator.class)
@Singleton
public class CustomClaimsGenerator extends JWTClaimsSetGenerator {
    private static final Logger LOG = LoggerFactory.getLogger(CustomClaimsGenerator.class);
    private static final String ROLES_KEY = "rolesKey";

    private final TokenConfiguration tokenConfiguration;
    private final JwtIdGenerator jwtIdGenerator;
    private final ClaimsAudienceProvider claimsAudienceProvider;
    private final String appName;

    /**
     * @param tokenConfiguration       Token Configuration
     * @param jwtIdGenerator           Generator which creates unique JWT ID
     * @param claimsAudienceProvider   Provider which identifies the recipients that the JWT is intended for.
     * @param applicationConfiguration The application configuration
     */
    public CustomClaimsGenerator(TokenConfiguration tokenConfiguration,@Nullable JwtIdGenerator jwtIdGenerator, @Nullable ClaimsAudienceProvider claimsAudienceProvider, ApplicationConfiguration applicationConfiguration) {
        super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider, applicationConfiguration);

        this.tokenConfiguration = tokenConfiguration;
        this.jwtIdGenerator = jwtIdGenerator;
        this.claimsAudienceProvider = claimsAudienceProvider;
        this.appName = applicationConfiguration != null ? applicationConfiguration.getName().orElse(Environment.MICRONAUT) : Environment.MICRONAUT;
    }
    
    /**
     * @param authentication Authenticated user's representation.
     * @param expiration  expiration time in seconds
     * @return The authentication claims
     */
    @Override
    public Map<String, Object> generateClaims(Authentication authentication, @Nullable Integer expiration) {

        expiration = 1000; //works with this value 

        JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();

        populateIat(builder);
        populateExp(builder, 15);
        populateJti(builder);
        populateIss(builder);
        populateAud(builder);
        populateNbf(builder);
        populateWithAuthentication(builder, authentication);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Generated claim set: {}", builder.build().toJSONObject());
        }
        return builder.build().getClaims();
    }

    /**
     * Populates iss claim.
     *
     * @param builder The Claims Builder
     * @see <a href="https://tools.ietf.org/html/rfc7519#section-4.1.1">iss (Issuer) Claim</a>
     */
    protected void populateIss(JWTClaimsSet.Builder builder) {
        if (appName != null) {
            builder.issuer(appName); // iss
        }
    }

    /**
     * Populates sub claim.
     *
     * @param builder     The Claims Builder
     * @param authentication Authenticated user's representation.
     * @see <a href="https://tools.ietf.org/html/rfc7519#section-4.1.2">sub (Subject) Claim</a>
     */
    protected void populateSub(JWTClaimsSet.Builder builder, Authentication authentication) {
        builder.subject(authentication.getName()); // sub
    }

    /**
     * Populates aud claim.
     *
     * @param builder The Claims Builder
     * @see <a href="https://tools.ietf.org/html/rfc7519#section-4.1.3">aud (Audience) Claim</a>
     */
    protected void populateAud(JWTClaimsSet.Builder builder) {
        if (claimsAudienceProvider != null) {
            builder.audience(claimsAudienceProvider.audience()); // aud
        }
    }

    /**
     * Populates exp claim.
     *
     * @param builder    The Claims Builder
     * @param expiration expiration time in seconds
     * @see <a href="https://tools.ietf.org/html/rfc7519#section-4.1.4">exp (ExpirationTime) Claim</a>
     */
    protected void populateExp(JWTClaimsSet.Builder builder, @Nullable Integer expiration) {
        if (expiration != null) {

            LOG.debug("Setting expiration to {}", expiration);
            System.out.print(Date.from(Instant.now().plus(expiration, ChronoUnit.SECONDS)));
            builder.expirationTime(Date.from(Instant.now().plus(expiration, ChronoUnit.SECONDS))); // exp
        }
    }

    /**
     * Populates nbf claim.
     *
     * @param builder The Claims Builder
     * @see <a href="https://tools.ietf.org/html/rfc7519#section-4.1.5">nbf (Not Before) Claim</a>
     */
    protected void populateNbf(JWTClaimsSet.Builder builder) {
        builder.notBeforeTime(new Date()); // nbf
    }

    /**
     * Populates iat claim.
     *
     * @param builder The Claims Builder
     * @see <a href="https://tools.ietf.org/html/rfc7519#section-4.1.6">iat (Issued At) Claim</a>
     */
    protected void populateIat(JWTClaimsSet.Builder builder) {
        builder.issueTime(new Date()); // iat
    }

    /**
     * Populates jti claim.
     *
     * @param builder The Claims Builder
     * @see <a href="https://tools.ietf.org/html/rfc7519#section-4.1.7">jti (JWT ID) Claim</a>
     */
    protected void populateJti(JWTClaimsSet.Builder builder) {
        if (jwtIdGenerator != null) {
            builder.jwtID(jwtIdGenerator.generateJtiClaim()); // jti
        }
    }

    /**
     * Populates Claims with Authentication object.
     *
     * @param builder     the Claims Builder
     * @param authentication Authenticated user's representation.
     */
    protected void populateWithAuthentication(JWTClaimsSet.Builder builder, Authentication authentication) {
        populateSub(builder, authentication);
        authentication.getAttributes().forEach(builder::claim);
        String rolesKey = tokenConfiguration.getRolesName();
        if (!rolesKey.equalsIgnoreCase(TokenConfiguration.DEFAULT_ROLES_NAME)) {
            builder.claim(ROLES_KEY, rolesKey);
        }
        builder.claim(rolesKey, authentication.getRoles());
    }

    /**
     * @param oldClaims  The old claims to use as a base in the new token generation.
     * @param expiration expiration time in seconds
     * @return Instance of {@link JWTClaimsSet}
     */
    @Override
    public Map<String, Object> generateClaimsSet(Map<String, ?> oldClaims, Integer expiration) {
        
        JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
        List<String> excludedClaims = Arrays.asList(JwtClaims.EXPIRATION_TIME, JwtClaims.ISSUED_AT, JwtClaims.NOT_BEFORE);
        for (String k : oldClaims.keySet()
                .stream()
                .filter(p -> !excludedClaims.contains(p))
                .collect(Collectors.toList())) {
            builder.claim(k, oldClaims.get(k));
        }
        populateExp(builder, expiration);
        populateIat(builder);
        populateNbf(builder);
        return builder.build().getClaims();
    }
}
  • Related