From 402e56840652cc9459d154144d8509a635bfd84c Mon Sep 17 00:00:00 2001 From: xuanswe <24372782+xuanswe@users.noreply.github.com> Date: Wed, 29 May 2024 15:38:26 +0200 Subject: [PATCH 1/2] #691 `OAuth2TokenProvider` should allow dynamic `systemTime` --- .../mock/oauth2/token/OAuth2TokenProvider.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProvider.kt b/src/main/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProvider.kt index a8f12daf..b3a67390 100644 --- a/src/main/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProvider.kt +++ b/src/main/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProvider.kt @@ -19,12 +19,23 @@ import java.time.Instant import java.util.Date import java.util.UUID +typealias TimeProvider = () -> Instant? + class OAuth2TokenProvider @JvmOverloads constructor( private val keyProvider: KeyProvider = KeyProvider(), - val systemTime: Instant? = null, + private val timeProvider: TimeProvider, ) { + val systemTime + get() = timeProvider() + + @JvmOverloads + constructor( + keyProvider: KeyProvider = KeyProvider(), + systemTime: Instant? = null, + ) : this(keyProvider, { systemTime }) + @JvmOverloads fun publicJwkSet(issuerId: String = "default"): JWKSet { return JWKSet(keyProvider.signingKey(issuerId)).toPublicJWKSet() From d1147d15480daedd6d4d6d534e9252e07f57c31f Mon Sep 17 00:00:00 2001 From: xuanswe <24372782+xuanswe@users.noreply.github.com> Date: Wed, 29 May 2024 16:48:17 +0200 Subject: [PATCH 2/2] #691 implement test for timeProvider --- .../token/OAuth2TokenProviderRSATest.kt | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProviderRSATest.kt b/src/test/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProviderRSATest.kt index 8094023e..9235f249 100644 --- a/src/test/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProviderRSATest.kt +++ b/src/test/kotlin/no/nav/security/mock/oauth2/token/OAuth2TokenProviderRSATest.kt @@ -16,7 +16,9 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource +import java.time.Clock import java.time.Instant +import java.time.ZoneId import java.time.temporal.ChronoUnit import java.util.Date @@ -99,8 +101,8 @@ internal class OAuth2TokenProviderRSATest { @Test fun `token should have issuedAt set to systemTime if set, otherwise use now()`() { - val yesterDay = Instant.now().minus(1, ChronoUnit.DAYS) - val tokenProvider = OAuth2TokenProvider(systemTime = yesterDay) + val yesterday = Instant.now().minus(1, ChronoUnit.DAYS) + val tokenProvider = OAuth2TokenProvider(systemTime = yesterday) tokenProvider.exchangeAccessToken( tokenRequest = @@ -118,6 +120,70 @@ internal class OAuth2TokenProviderRSATest { } } + @Test + fun `token should have issuedAt set dynamically according to timeProvider`() { + val clock = + object : Clock() { + private var clock = systemDefaultZone() + + override fun instant() = clock.instant() + + override fun withZone(zone: ZoneId) = clock.withZone(zone) + + override fun getZone() = clock.zone + + fun fixed(instant: Instant) { + clock = fixed(instant, zone) + } + } + + val tokenProvider = OAuth2TokenProvider { clock.instant() } + + val instant1 = Instant.parse("2000-12-03T10:15:30.00Z") + val instant2 = Instant.parse("2020-01-21T00:00:00.00Z") + instant1 shouldNotBe instant2 + + run { + clock.fixed(instant1) + tokenProvider.systemTime shouldBe instant1 + + tokenProvider.exchangeAccessToken( + tokenRequest = + nimbusTokenRequest( + "id", + "grant_type" to GrantType.CLIENT_CREDENTIALS.value, + "scope" to "scope1", + ), + issuerUrl = "http://default_if_not_overridden".toHttpUrl(), + claimsSet = tokenProvider.jwt(mapOf()).jwtClaimsSet, + oAuth2TokenCallback = DefaultOAuth2TokenCallback(), + ) + }.asClue { + it.jwtClaimsSet.issueTime shouldBe Date.from(instant1) + println(it.serialize()) + } + + run { + clock.fixed(instant2) + tokenProvider.systemTime shouldBe instant2 + + tokenProvider.exchangeAccessToken( + tokenRequest = + nimbusTokenRequest( + "id", + "grant_type" to GrantType.CLIENT_CREDENTIALS.value, + "scope" to "scope1", + ), + issuerUrl = "http://default_if_not_overridden".toHttpUrl(), + claimsSet = tokenProvider.jwt(mapOf()).jwtClaimsSet, + oAuth2TokenCallback = DefaultOAuth2TokenCallback(), + ) + }.asClue { + it.jwtClaimsSet.issueTime shouldBe Date.from(instant2) + println(it.serialize()) + } + } + private fun idToken(issuerUrl: String): SignedJWT = tokenProvider.idToken( tokenRequest =