From 47e78d0d26e46af5709a84d0a3d0c80e00ccbb9a Mon Sep 17 00:00:00 2001 From: Dylan De Faoite Date: Tue, 13 Oct 2026 19:26:33 +0100 Subject: [PATCH] add returnsNewToken for grace period check --- .../vodsystem/security/TokenServiceTest.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/ddf/vodsystem/security/TokenServiceTest.java b/src/test/java/com/ddf/vodsystem/security/TokenServiceTest.java index a9e56ef..516f722 100644 --- a/src/test/java/com/ddf/vodsystem/security/TokenServiceTest.java +++ b/src/test/java/com/ddf/vodsystem/security/TokenServiceTest.java @@ -74,7 +74,7 @@ class TokenServiceTest { Duration.ofSeconds(10), Duration.ofSeconds(10), Duration.ofSeconds(10), - Duration.ofSeconds(10) + Duration.ofSeconds(5) ) ); @@ -323,5 +323,44 @@ class TokenServiceTest { TokenService.Rotation rotation = tokenService.rotate(rawToken); assertNotEquals(rawToken, rotation.newRefreshToken()); } + + // Grace period checks + @Test + void usedWithinGrace_returnsNewToken() { + Instant usedAt = Instant.now(clock); + + TokenFamily tokenFamily = new TokenFamily(); + tokenFamily.setId(1L); + tokenFamily.setUser(createUser(1L, "user", "user")); + tokenFamily.setExpiresAt(usedAt.plusSeconds(10)); + + RefreshToken refreshToken = new RefreshToken(); + refreshToken.setTokenHash(rawToken.getBytes()); + refreshToken.setTokenFamily(tokenFamily); + refreshToken.setExpiresAt(usedAt.plusSeconds(10)); + refreshToken.setUsedAt(usedAt); + + when(refreshTokenRepository.findByHash(any())) + .thenReturn(Optional.of(refreshToken)); + + // within grace: 3s since usedAt, grace is 5s, and nothing has expired yet + clock.advance(Duration.ofSeconds(3)); + + TokenService.Rotation rotation = tokenService.rotate(rawToken); + + assertEquals(1L, rotation.userId()); + assertNotEquals(rawToken, rotation.newRefreshToken()); + assertNull(tokenFamily.getRevokedAt()); + verify(tokenFamilyRepository, never()).save(any()); + + // grace is not extended by a replay, and only the new token is written + assertEquals(usedAt, refreshToken.getUsedAt()); + + verify(refreshTokenRepository, times(1)).save(tokenCaptor.capture()); + RefreshToken newToken = tokenCaptor.getValue(); + assertNull(newToken.getUsedAt()); + assertSame(tokenFamily, newToken.getTokenFamily()); + assertNotEquals(refreshToken.getTokenHash(), newToken.getTokenHash()); + } } } \ No newline at end of file