Working with JWT Authentication

1. Understanding JWT Structure

JWT Anatomy

HEADER.PAYLOAD.SIGNATURE
  {alg:"HS256",typ:"JWT"} . {sub:"123",iat:…,exp:…} . HMAC-SHA256(...)
Claim Meaning
iss Issuer
sub Subject (user id)
aud Audience
exp Expiry (unix sec)
iat Issued at
nbf Not-before
jti Token id (revocation)

2. Creating JWT Tokens

Example: Issue JWT with claims and expiry

@Service
public class JwtIssuer {
  private final JwtEncoder encoder;
  public JwtIssuer(JwtEncoder encoder) { this.encoder = encoder; }
  public String issue(String subject, Set<String> roles) {
    Instant now = Instant.now();
    JwtClaimsSet claims = JwtClaimsSet.builder()
      .issuer("https://auth.example.com")
      .issuedAt(now).expiresAt(now.plus(15, ChronoUnit.MINUTES))
      .subject(subject).claim("roles", roles)
      .build();
    return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
  }
}

3. Validating and Parsing JWT Tokens

Example: JWT decoder with secret key

@Bean
JwtDecoder jwtDecoder() {
  return NimbusJwtDecoder.withSecretKey(secretKey).build(); // HS256
}
// Auto-validation: signature, exp, nbf, iss

4. Implementing JWT Authentication Filter

Approach Recommendation
Spring Security OAuth2 Resource Server Preferred — handles filter, validation, cache
Custom OncePerRequestFilter Only when special token format needed

Example: Configure JWT resource server

http.oauth2ResourceServer(o -> o.jwt(j -> j.decoder(jwtDecoder())
                                            .jwtAuthenticationConverter(converter())));

5. Configuring Token Expiration and Refresh

Token Typical TTL
Access 5–15 minutes
Refresh 7–30 days, rotated
ID token (OIDC) Same as access

6. Storing User Details in JWT Claims

Example: Map roles claim to authorities

@Bean
JwtAuthenticationConverter converter() {
  JwtGrantedAuthoritiesConverter ga = new JwtGrantedAuthoritiesConverter();
  ga.setAuthoritiesClaimName("roles");
  ga.setAuthorityPrefix("ROLE_");
  JwtAuthenticationConverter c = new JwtAuthenticationConverter();
  c.setJwtGrantedAuthoritiesConverter(ga);
  return c;
}

7. Handling Token-Based Authorization

Example: Access JWT claims in controller

@RestController
public class MeController {
  @GetMapping("/me")
  public Map<String,Object> me(@AuthenticationPrincipal Jwt jwt) {
    return Map.of("sub", jwt.getSubject(), "roles", jwt.getClaim("roles"));
  }
}

8. Implementing Refresh Token Mechanism

Refresh Token Rotation

  1. Login → issue access (15m) + refresh (7d, store hash)
  2. Client calls /auth/refresh with refresh token
  3. Server validates, revokes used refresh, issues new pair
  4. Detect reuse → revoke entire family (token theft)

9. Securing JWT Secret Keys

Practice Reason
Min 256-bit secret (HS256) Required for HMAC strength
Store in vault / secret manager Never in source / config
Rotate periodically Limit blast radius
Use kid header Support multiple keys for rotation

10. Handling Token Revocation Strategies

Strategy Trade-off
Short TTL (no blacklist) Simple; brief window of misuse
Redis blacklist by jti Strong revocation; extra lookup
Refresh token DB Revoke long-lived tokens
Token versioning per user Bump version → all tokens invalid

11. Using RS256 vs HS256 Algorithms

HS256 (HMAC-SHA256)

  • Symmetric secret
  • Faster, smaller
  • Issuer = verifier
  • Not for distributed verifiers

RS256 (RSA-SHA256)

  • Asymmetric (private/public)
  • Verifiers use JWKS
  • Suited for OAuth/OIDC
  • Larger tokens