diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java index fc76c77..9e4420c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java @@ -118,7 +118,7 @@ public class OAuth2AuthorizationCodeAuthenticationProvider implements Authentica Set authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES); Jwt jwt = OAuth2TokenIssuerUtil - .issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), authorizedScopes); + .issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), authorizedScopes, registeredClient.getTokenSettings().accessTokenTimeToLive()); OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), authorizedScopes); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java index 3d5e160..bf948db 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java @@ -94,7 +94,7 @@ public class OAuth2ClientCredentialsAuthenticationProvider implements Authentica } Jwt jwt = OAuth2TokenIssuerUtil - .issueJwtAccessToken(this.jwtEncoder, clientPrincipal.getName(), registeredClient.getClientId(), scopes); + .issueJwtAccessToken(this.jwtEncoder, clientPrincipal.getName(), registeredClient.getClientId(), scopes, registeredClient.getTokenSettings().accessTokenTimeToLive()); OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), scopes); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java index 9f50b1f..dc3330b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java @@ -114,7 +114,7 @@ public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationP } Jwt jwt = OAuth2TokenIssuerUtil - .issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), scopes); + .issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), scopes, registeredClient.getTokenSettings().accessTokenTimeToLive()); OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), scopes); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIssuerUtil.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIssuerUtil.java index 603fc98..07734fa 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIssuerUtil.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIssuerUtil.java @@ -28,7 +28,6 @@ import org.springframework.security.oauth2.jwt.JwtEncoder; import java.time.Duration; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.Base64; import java.util.Collections; import java.util.Set; @@ -41,12 +40,12 @@ class OAuth2TokenIssuerUtil { private static final StringKeyGenerator TOKEN_GENERATOR = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); - static Jwt issueJwtAccessToken(JwtEncoder jwtEncoder, String subject, String audience, Set scopes) { + static Jwt issueJwtAccessToken(JwtEncoder jwtEncoder, String subject, String audience, Set scopes, Duration tokenTimeToLive) { JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); String issuer = "https://oauth2.provider.com"; // TODO Allow configuration for issuer claim Instant issuedAt = Instant.now(); - Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); // TODO Allow configuration for access token time-to-live + Instant expiresAt = issuedAt.plus(tokenTimeToLive); JwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder() .issuer(issuer) @@ -61,9 +60,9 @@ class OAuth2TokenIssuerUtil { return jwtEncoder.encode(joseHeader, jwtClaimsSet); } - static OAuth2RefreshToken issueRefreshToken(Duration refreshTokenTimeToLive) { + static OAuth2RefreshToken issueRefreshToken(Duration tokenTimeToLive) { Instant issuedAt = Instant.now(); - Instant expiresAt = issuedAt.plus(refreshTokenTimeToLive); + Instant expiresAt = issuedAt.plus(tokenTimeToLive); return new OAuth2RefreshToken2(TOKEN_GENERATOR.generateKey(), issuedAt, expiresAt); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java index 9cccc96..e86547c 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java @@ -251,10 +251,12 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { } @Test - public void authenticateWhenRefreshTokenTimeToLiveConfiguredThenRefreshTokenExpirySet() { + public void authenticateWhenTokenTimeToLiveConfiguredThenTokenExpirySet() { + Duration accessTokenTTL = Duration.ofHours(2); Duration refreshTokenTTL = Duration.ofDays(1); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .tokenSettings(tokenSettings -> tokenSettings.refreshTokenTimeToLive(refreshTokenTTL)) + .tokenSettings(tokenSettings -> + tokenSettings.accessTokenTimeToLive(accessTokenTTL).refreshTokenTimeToLive(refreshTokenTTL)) .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); @@ -267,7 +269,9 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); - when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt()); + Instant accessTokenIssuedAt = Instant.now(); + Instant accessTokenExpiresAt = accessTokenIssuedAt.plus(accessTokenTTL); + when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt(accessTokenIssuedAt, accessTokenExpiresAt)); OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); @@ -276,6 +280,11 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); + assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getTokens().getAccessToken()); + Instant expectedAccessTokenExpiresAt = accessTokenAuthentication.getAccessToken().getIssuedAt().plus(accessTokenTTL); + assertThat(accessTokenAuthentication.getAccessToken().getExpiresAt()).isBetween( + expectedAccessTokenExpiresAt.minusSeconds(1), expectedAccessTokenExpiresAt.plusSeconds(1)); + assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getTokens().getRefreshToken()); Instant expectedRefreshTokenExpiresAt = accessTokenAuthentication.getRefreshToken().getIssuedAt().plus(refreshTokenTTL); assertThat(accessTokenAuthentication.getRefreshToken().getExpiresAt()).isBetween( @@ -309,6 +318,10 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { private static Jwt createJwt() { Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); + return createJwt(issuedAt, expiresAt); + } + + private static Jwt createJwt(Instant issuedAt, Instant expiresAt) { return Jwt.withTokenValue("token") .header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()) .issuedAt(issuedAt)