Skip to content

Commit

Permalink
Copy JWT iat from incoming token
Browse files Browse the repository at this point in the history
To avoid having problems when just-expired tokens are accepted, copy the iat claim from the incoming token.
Additionally, add a re-iat (re-issued at) claim containing the timestamp that the gateway has generated
a replacement token
  • Loading branch information
vierbergenlars committed Oct 3, 2024
1 parent ff4bc73 commit 841001b
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ private Mono<JWTClaimsSet> createClaims(ServerWebExchange exchange, Authenticati
.filter(exp -> exp.compareTo(maxExpiration) <= 0)
.orElse(maxExpiration));
}))
.issueTime(Objects.requireNonNullElseGet(claims.getIssueTime(), Date::new))
.claim("re-iat", new Date())
.issueTime(Objects.requireNonNullElseGet(claims.getIssueTime(), () -> {
return findIssuedAtTime(authentication)
.map(Date::from)
.orElseGet(Date::new);
}))
.build();
});
}
Expand All @@ -116,6 +121,16 @@ private Optional<Instant> findExpirationTime(Authentication authentication) {
return Optional.empty();
}

private Optional<Instant> findIssuedAtTime(Authentication authentication) {
var principal = authentication.getPrincipal();
if(principal instanceof Jwt jwt) {
return Optional.ofNullable(jwt.getIssuedAt());
} else if(principal instanceof OidcUser oidcUser) {
return Optional.ofNullable(oidcUser.getIdToken().getIssuedAt());
}
return Optional.empty();
}

private Map<String, Object> reconstructActorChain(Actor actor) {
var claims = new HashMap<>(actor.getClaims().getClaims());
if(actor.getParent() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,15 @@ void creates_derived_jwt_for_delegation_token() {
@Test
void creates_derived_jwt_for_oidc_user() {
var issuer = new SignedJwtIssuer(CLAIMS_SIGNER, JwtClaimsResolver.empty());

var iat = Instant.now().minus(3, ChronoUnit.MINUTES);
var exp = Instant.now().plus(1, ChronoUnit.MINUTES);
var oidcUser = new DefaultOidcUser(
List.of(),
OidcIdToken.withTokenValue("XXX")
.subject("my-user")
.issuedAt(iat)
.expiresAt(exp)
.build()
);

Expand All @@ -110,8 +115,8 @@ void creates_derived_jwt_for_oidc_user() {
assertThat(issuer.issueSubstitutionToken(exchange).block()).isInstanceOfSatisfying(Jwt.class, token -> {
assertThat(token.getIssuer()).hasToString("https://upstream-issuer.example");
assertThat(token.getSubject()).isEqualTo("my-user");
assertThat(token.getIssuedAt()).isBeforeOrEqualTo(Instant.now());
assertThat(token.getExpiresAt()).isBetween(Instant.now().plus(4, ChronoUnit.MINUTES), Instant.now().plus(5, ChronoUnit.MINUTES));
assertThat(token.getIssuedAt()).isCloseTo(iat, within(1, ChronoUnit.SECONDS));
assertThat(token.getExpiresAt()).isCloseTo(exp, within(1, ChronoUnit.SECONDS));
assertThat(token.getTokenValue()).satisfies(verifyJwtSignedBy(issuer));
});
}
Expand Down Expand Up @@ -179,6 +184,33 @@ void new_jwt_with_expiry_shorter_than_max() {
});
}

@Test
void derived_jwt_for_just_expired_jwt() {
var issuer = new SignedJwtIssuer(CLAIMS_SIGNER, JwtClaimsResolver.empty());

var iat = Instant.now().minus(5, ChronoUnit.MINUTES);
var expiry = Instant.now().minus(10, ChronoUnit.SECONDS);

var exchange = createExchange(
new JwtAuthenticationToken(Jwt.withTokenValue("XXXX")
.header("alg", "RS256")
.issuedAt(iat)
.expiresAt(expiry)
.build(),
List.of(new PrincipalAuthenticationDetailsGrantedAuthority(new Actor(
ActorType.USER,
() -> Map.of("iss", "https://upstream-issuer.example", "sub", "my-user"),
null
)))
)
);

assertThat(issuer.issueSubstitutionToken(exchange).block()).isInstanceOfSatisfying(Jwt.class, token -> {
assertThat(token.getIssuedAt()).isCloseTo(iat, within(1, ChronoUnit.SECONDS));
assertThat(token.getExpiresAt()).isCloseTo(expiry, within(1, ChronoUnit.SECONDS));
});
}

static ServerWebExchange createExchange(Authentication authentication) {
var request = MockServerHttpRequest.get("/").build();
var securityContext = new SecurityContextImpl(authentication);
Expand Down

0 comments on commit 841001b

Please sign in to comment.