diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java index 564baf0..ab94ea7 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java @@ -144,16 +144,18 @@ public final class OAuth2AuthorizationServerConfigurerSection 1.5 Refresh Token + * @see Section 1.5 Refresh Token Grant * @see Section 6 Refreshing an Access Token */ - public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider { - private final OAuth2AuthorizationService authorizationService; private final JwtEncoder jwtEncoder; - public OAuth2RefreshTokenAuthenticationProvider(OAuth2AuthorizationService authorizationService, JwtEncoder jwtEncoder) { + /** + * Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided parameters. + * + * @param authorizationService the authorization service + * @param jwtEncoder the jwt encoder + */ + public OAuth2RefreshTokenAuthenticationProvider(OAuth2AuthorizationService authorizationService, + JwtEncoder jwtEncoder) { Assert.notNull(authorizationService, "authorizationService cannot be null"); Assert.notNull(jwtEncoder, "jwtEncoder cannot be null"); - this.authorizationService = authorizationService; this.jwtEncoder = jwtEncoder; } @@ -73,43 +76,45 @@ public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationP if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(refreshTokenAuthentication.getPrincipal().getClass())) { clientPrincipal = (OAuth2ClientAuthenticationToken) refreshTokenAuthentication.getPrincipal(); } - if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) { throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT)); } + RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); - OAuth2Authorization authorization = this.authorizationService.findByToken(refreshTokenAuthentication.getRefreshToken(), TokenType.REFRESH_TOKEN); + OAuth2Authorization authorization = this.authorizationService.findByToken( + refreshTokenAuthentication.getRefreshToken(), TokenType.REFRESH_TOKEN); if (authorization == null) { throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT)); } - Instant refreshTokenExpiration = authorization.getTokens().getRefreshToken().getExpiresAt(); - if (refreshTokenExpiration.isBefore(Instant.now())) { - // as per https://tools.ietf.org/html/rfc6749#section-5.2 - // invalid_grant: The provided authorization grant (e.g., authorization - // code, resource owner credentials) or refresh token is invalid, expired, revoked [...]. + if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) { + throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT)); + } + + Instant refreshTokenExpiresAt = authorization.getTokens().getRefreshToken().getExpiresAt(); + if (refreshTokenExpiresAt.isBefore(Instant.now())) { + // As per https://tools.ietf.org/html/rfc6749#section-5.2 + // invalid_grant: The provided authorization grant (e.g., authorization code, + // resource owner credentials) or refresh token is invalid, expired, revoked [...]. throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT)); } - RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); - - // https://tools.ietf.org/html/rfc6749#section-6 + // As per https://tools.ietf.org/html/rfc6749#section-6 // The requested scope MUST NOT include any scope not originally granted by the resource owner, // and if omitted is treated as equal to the scope originally granted by the resource owner. - Set refreshTokenScopes = refreshTokenAuthentication.getScopes(); + Set scopes = refreshTokenAuthentication.getScopes(); Set authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES); - if (!authorizedScopes.containsAll(refreshTokenScopes)) { + if (!authorizedScopes.containsAll(scopes)) { throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE)); } - - if (refreshTokenScopes.isEmpty()) { - refreshTokenScopes = authorizedScopes; + if (scopes.isEmpty()) { + scopes = authorizedScopes; } Jwt jwt = OAuth2TokenIssuerUtil - .issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), refreshTokenScopes); + .issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), scopes); OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), refreshTokenScopes); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), scopes); TokenSettings tokenSettings = registeredClient.getTokenSettings(); OAuth2RefreshToken refreshToken; @@ -120,13 +125,13 @@ public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationP } authorization = OAuth2Authorization.from(authorization) - .attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt) - .tokens(OAuth2Tokens.builder().accessToken(accessToken).refreshToken(refreshToken).build()) - .build(); - + .tokens(OAuth2Tokens.builder().accessToken(accessToken).refreshToken(refreshToken).build()) + .attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt) + .build(); this.authorizationService.save(authorization); - return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken); + return new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken, refreshToken); } @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java index 30770e3..15c86df 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.security.oauth2.server.authorization.authentication; -import java.util.Collections; -import java.util.Set; - import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.server.authorization.Version; import org.springframework.util.Assert; +import java.util.Collections; +import java.util.Set; + /** * An {@link Authentication} implementation used for the OAuth 2.0 Refresh Token Grant. * @@ -33,42 +33,37 @@ import org.springframework.util.Assert; * @see OAuth2ClientAuthenticationToken */ public class OAuth2RefreshTokenAuthenticationToken extends AbstractAuthenticationToken { - - private final Authentication clientPrincipal; + private static final long serialVersionUID = Version.SERIAL_VERSION_UID; private final String refreshToken; + private final Authentication clientPrincipal; private final Set scopes; /** * Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters. * - * @param refreshToken refresh token value + * @param refreshToken the refresh token * @param clientPrincipal the authenticated client principal */ public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal) { - this(clientPrincipal, refreshToken, Collections.emptySet()); + this(refreshToken, clientPrincipal, Collections.emptySet()); } /** * Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters. * + * @param refreshToken the refresh token * @param clientPrincipal the authenticated client principal - * @param refreshToken refresh token value - * @param requestedScopes scopes requested by refresh token + * @param scopes the requested scope(s) */ - public OAuth2RefreshTokenAuthenticationToken(Authentication clientPrincipal, String refreshToken, Set requestedScopes) { + public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal, + Set scopes) { super(Collections.emptySet()); - + Assert.hasText(refreshToken, "refreshToken cannot be empty"); Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); - Assert.hasText(refreshToken, "refreshToken cannot be null or empty"); - - this.clientPrincipal = clientPrincipal; + Assert.notNull(scopes, "scopes cannot be null"); this.refreshToken = refreshToken; - this.scopes = requestedScopes; - } - - @Override - public Object getCredentials() { - return ""; + this.clientPrincipal = clientPrincipal; + this.scopes = scopes; } @Override @@ -76,6 +71,16 @@ public class OAuth2RefreshTokenAuthenticationToken extends AbstractAuthenticatio return this.clientPrincipal; } + @Override + public Object getCredentials() { + return ""; + } + + /** + * Returns the refresh token. + * + * @return the refresh token + */ public String getRefreshToken() { return this.refreshToken; } 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 b473c14..df217c3 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 @@ -13,19 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.security.oauth2.server.authorization.authentication; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -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; - import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; import org.springframework.security.crypto.keygen.StringKeyGenerator; import org.springframework.security.oauth2.core.OAuth2RefreshToken; @@ -36,13 +25,23 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtClaimsSet; import org.springframework.security.oauth2.jwt.JwtEncoder; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +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; + /** * @author Alexey Nesterov * @since 0.0.3 */ class OAuth2TokenIssuerUtil { - private static final StringKeyGenerator CODE_GENERATOR = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); + private static final StringKeyGenerator TOKEN_GENERATOR = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); static Jwt issueJwtAccessToken(JwtEncoder jwtEncoder, String subject, String audience, Set scopes) { JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build(); @@ -71,8 +70,8 @@ class OAuth2TokenIssuerUtil { static OAuth2RefreshToken issueRefreshToken(Duration refreshTokenTimeToLive) { Instant issuedAt = Instant.now(); - Instant refreshTokenExpiresAt = issuedAt.plus(refreshTokenTimeToLive); + Instant expiresAt = issuedAt.plus(refreshTokenTimeToLive); - return new OAuth2RefreshToken(CODE_GENERATOR.generateKey(), issuedAt, refreshTokenExpiresAt); + return new OAuth2RefreshToken(TOKEN_GENERATOR.generateKey(), issuedAt, expiresAt); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/TokenSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/TokenSettings.java index ad69a73..63fd75c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/TokenSettings.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/TokenSettings.java @@ -15,12 +15,12 @@ */ package org.springframework.security.oauth2.server.authorization.config; +import org.springframework.util.Assert; + import java.time.Duration; import java.util.HashMap; import java.util.Map; -import org.springframework.util.Assert; - /** * A facility for token configuration settings. * @@ -61,33 +61,31 @@ public class TokenSettings extends Settings { } /** - * Set the time-to-live for an access token. + * Set the time-to-live for an access token. Must be greater than {@code Duration.ZERO}. * * @param accessTokenTimeToLive the time-to-live for an access token * @return the {@link TokenSettings} */ public TokenSettings accessTokenTimeToLive(Duration accessTokenTimeToLive) { + Assert.notNull(accessTokenTimeToLive, "accessTokenTimeToLive cannot be null"); + Assert.isTrue(accessTokenTimeToLive.getSeconds() > 0, "accessTokenTimeToLive must be greater than Duration.ZERO"); setting(ACCESS_TOKEN_TIME_TO_LIVE, accessTokenTimeToLive); return this; } /** - * Returns {@code true} if refresh tokens support is enabled. - * This include generation of refresh token as a part of Authorization Code Grant flow and support of Refresh Token - * Grant flow. The default is {@code true}. + * Returns {@code true} if refresh tokens are enabled. The default is {@code true}. * - * @return {@code true} if the client support refresh token, {@code false} otherwise + * @return {@code true} if refresh tokens are enabled, {@code false} otherwise */ public boolean enableRefreshTokens() { return setting(ENABLE_REFRESH_TOKENS); } /** - * Set to {@code true} to enable refresh tokens support. - * This include generation of refresh token as a part of Authorization Code Grant flow and support of Refresh Token - * Grant flow. + * Set to {@code true} to enable refresh tokens. * - * @param enableRefreshTokens {@code true} to enable refresh token grant support, {@code false} otherwise + * @param enableRefreshTokens {@code true} to enable refresh tokens, {@code false} otherwise * @return the {@link TokenSettings} */ public TokenSettings enableRefreshTokens(boolean enableRefreshTokens) { @@ -96,18 +94,19 @@ public class TokenSettings extends Settings { } /** - * Returns {@code true} if existing refresh token is re-used when a new access token is requested via Refresh Token grant, - * or {@code false} if a new refresh token is generated. - * The default is {@code false}. + * Returns {@code true} if refresh tokens are reused when returning the access token response, + * or {@code false} if a new refresh token is issued. The default is {@code true}. */ public boolean reuseRefreshTokens() { return setting(REUSE_REFRESH_TOKENS); } /** - * Set to {@code true} to re-use existing refresh token when new access token is requested via Refresh Token grant, - * or to {@code false} to generate a new refresh token. - * @param reuseRefreshTokens {@code true} to re-use existing refresh token, {@code false} to generate a new one + * Set to {@code true} if refresh tokens are reused when returning the access token response, + * or {@code false} if a new refresh token is issued. + * + * @param reuseRefreshTokens {@code true} to reuse refresh tokens, {@code false} to issue new refresh tokens + * @return the {@link TokenSettings} */ public TokenSettings reuseRefreshTokens(boolean reuseRefreshTokens) { setting(REUSE_REFRESH_TOKENS, reuseRefreshTokens); @@ -115,21 +114,23 @@ public class TokenSettings extends Settings { } /** - * Returns refresh token time-to-live. The default is 60 minutes. Always greater than {@code Duration.ZERO}. - * @return refresh token time-to-live + * Returns the time-to-live for a refresh token. The default is 60 minutes. + * + * @return the time-to-live for a refresh token */ public Duration refreshTokenTimeToLive() { return setting(REFRESH_TOKEN_TIME_TO_LIVE); } /** - * Sets refresh token time-to-live. - * @param refreshTokenTimeToLive refresh token time-to-live. Has to be greater than {@code Duration.ZERO}. + * Set the time-to-live for a refresh token. Must be greater than {@code Duration.ZERO}. + * + * @param refreshTokenTimeToLive the time-to-live for a refresh token + * @return the {@link TokenSettings} */ public TokenSettings refreshTokenTimeToLive(Duration refreshTokenTimeToLive) { Assert.notNull(refreshTokenTimeToLive, "refreshTokenTimeToLive cannot be null"); - Assert.isTrue(refreshTokenTimeToLive.getSeconds() > 0, "refreshTokenTimeToLive has to be greater than Duration.ZERO"); - + Assert.isTrue(refreshTokenTimeToLive.getSeconds() > 0, "refreshTokenTimeToLive must be greater than Duration.ZERO"); setting(REFRESH_TOKEN_TIME_TO_LIVE, refreshTokenTimeToLive); return this; } @@ -138,7 +139,7 @@ public class TokenSettings extends Settings { Map settings = new HashMap<>(); settings.put(ACCESS_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(5)); settings.put(ENABLE_REFRESH_TOKENS, true); - settings.put(REUSE_REFRESH_TOKENS, false); + settings.put(REUSE_REFRESH_TOKENS, true); settings.put(REFRESH_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(60)); return settings; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java index ed9d8ee..903ead7 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java @@ -130,8 +130,8 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter { this.tokenEndpointMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name()); Map> converters = new HashMap<>(); converters.put(AuthorizationGrantType.AUTHORIZATION_CODE, new AuthorizationCodeAuthenticationConverter()); - converters.put(AuthorizationGrantType.CLIENT_CREDENTIALS, new ClientCredentialsAuthenticationConverter()); converters.put(AuthorizationGrantType.REFRESH_TOKEN, new RefreshTokenAuthenticationConverter()); + converters.put(AuthorizationGrantType.CLIENT_CREDENTIALS, new ClientCredentialsAuthenticationConverter()); this.authorizationGrantAuthenticationConverter = new DelegatingAuthorizationGrantAuthenticationConverter(converters); } @@ -165,7 +165,9 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter { } } - private void sendAccessTokenResponse(HttpServletResponse response, OAuth2AccessToken accessToken, OAuth2RefreshToken refreshToken) throws IOException { + private void sendAccessTokenResponse(HttpServletResponse response, OAuth2AccessToken accessToken, + OAuth2RefreshToken refreshToken) throws IOException { + OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) .tokenType(accessToken.getTokenType()) @@ -235,6 +237,43 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter { } } + private static class RefreshTokenAuthenticationConverter implements Converter { + + @Override + public Authentication convert(HttpServletRequest request) { + // grant_type (REQUIRED) + String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); + if (!AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) { + return null; + } + + Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); + + MultiValueMap parameters = OAuth2EndpointUtils.getParameters(request); + + // refresh_token (REQUIRED) + String refreshToken = parameters.getFirst(OAuth2ParameterNames.REFRESH_TOKEN); + if (!StringUtils.hasText(refreshToken) || + parameters.get(OAuth2ParameterNames.REFRESH_TOKEN).size() != 1) { + throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REFRESH_TOKEN); + } + + // scope (OPTIONAL) + String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE); + if (StringUtils.hasText(scope) && + parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { + throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE); + } + if (StringUtils.hasText(scope)) { + Set requestedScopes = new HashSet<>( + Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); + return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal, requestedScopes); + } + + return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal); + } + } + private static class ClientCredentialsAuthenticationConverter implements Converter { @Override @@ -264,41 +303,4 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter { return new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal); } } - - private static class RefreshTokenAuthenticationConverter implements Converter { - - @Override - public Authentication convert(HttpServletRequest request) { - // grant_type (REQUIRED) - String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); - if (!AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) { - return null; - } - - Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); - - MultiValueMap parameters = OAuth2EndpointUtils.getParameters(request); - - // refresh token (REQUIRED) - String refreshToken = parameters.getFirst(OAuth2ParameterNames.REFRESH_TOKEN); - if (StringUtils.hasText(refreshToken) && - parameters.get(OAuth2ParameterNames.REFRESH_TOKEN).size() != 1) { - throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REFRESH_TOKEN); - } - - // scope (OPTIONAL) - String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE); - if (StringUtils.hasText(scope) && - parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { - throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE); - } - if (StringUtils.hasText(scope)) { - Set requestedScopes = new HashSet<>( - Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); - return new OAuth2RefreshTokenAuthenticationToken(clientPrincipal, refreshToken, requestedScopes); - } - - return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal); - } - } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java index 9b1b8fc..b715219 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java @@ -162,7 +162,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN)) .thenReturn(this.authorization); - RegisteredClient clientWithReuseTokensTrue = TestRegisteredClients.registeredClient() + RegisteredClient clientWithReuseTokensTrue = TestRegisteredClients.registeredClient2() .tokenSettings(tokenSettings -> tokenSettings.reuseRefreshTokens(true)) .build(); @@ -183,7 +183,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN)) .thenReturn(this.authorization); - RegisteredClient clientWithReuseTokensFalse = TestRegisteredClients.registeredClient() + RegisteredClient clientWithReuseTokensFalse = TestRegisteredClients.registeredClient2() .tokenSettings(tokenSettings -> tokenSettings.reuseRefreshTokens(false)) .build(); @@ -208,7 +208,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { requestedScopes.add("openid"); OAuth2RefreshTokenAuthenticationToken tokenWithScopes - = new OAuth2RefreshTokenAuthenticationToken(this.clientPrincipal, REFRESH_TOKEN_VALUE, requestedScopes); + = new OAuth2RefreshTokenAuthenticationToken(REFRESH_TOKEN_VALUE, this.clientPrincipal, requestedScopes); when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN)) .thenReturn(this.authorization); @@ -227,7 +227,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { requestedScopes.add("another-scope"); OAuth2RefreshTokenAuthenticationToken tokenWithScopes - = new OAuth2RefreshTokenAuthenticationToken(this.clientPrincipal, REFRESH_TOKEN_VALUE, requestedScopes); + = new OAuth2RefreshTokenAuthenticationToken(REFRESH_TOKEN_VALUE, this.clientPrincipal, requestedScopes); when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN)) .thenReturn(this.authorization); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java index e8912b4..c665748 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java @@ -34,7 +34,7 @@ public class OAuth2RefreshTokenAuthenticationTokenTests { @Test public void constructorWhenClientPrincipalNullThrowException() { - assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", null)) + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("test", null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("clientPrincipal cannot be null"); } @@ -43,18 +43,18 @@ public class OAuth2RefreshTokenAuthenticationTokenTests { public void constructorWhenRefreshTokenNullOrEmptyThrowException() { assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, mock(OAuth2ClientAuthenticationToken.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("refreshToken cannot be null or empty"); + .hasMessage("refreshToken cannot be empty"); assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", mock(OAuth2ClientAuthenticationToken.class))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("refreshToken cannot be null or empty"); + .hasMessage("refreshToken cannot be empty"); } @Test public void constructorWhenGettingScopesThenReturnRequestedScopes() { Set expectedScopes = new HashSet<>(Arrays.asList("scope-a", "scope-b")); OAuth2RefreshTokenAuthenticationToken token - = new OAuth2RefreshTokenAuthenticationToken(mock(OAuth2ClientAuthenticationToken.class), "test", expectedScopes); + = new OAuth2RefreshTokenAuthenticationToken("test", mock(OAuth2ClientAuthenticationToken.class), expectedScopes); assertThat(token.getScopes()).containsAll(expectedScopes); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/TokenSettingsTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/TokenSettingsTests.java index 5528b29..0076cbf 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/TokenSettingsTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/TokenSettingsTests.java @@ -35,7 +35,7 @@ public class TokenSettingsTests { assertThat(tokenSettings.settings()).hasSize(4); assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5)); assertThat(tokenSettings.enableRefreshTokens()).isTrue(); - assertThat(tokenSettings.reuseRefreshTokens()).isEqualTo(false); + assertThat(tokenSettings.reuseRefreshTokens()).isEqualTo(true); assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60)); } @@ -83,12 +83,12 @@ public class TokenSettingsTests { assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(Duration.ZERO)) .isInstanceOf(IllegalArgumentException.class) .extracting(Throwable::getMessage) - .isEqualTo("refreshTokenTimeToLive has to be greater than Duration.ZERO"); + .isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO"); assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(Duration.ofSeconds(-10))) .isInstanceOf(IllegalArgumentException.class) .extracting(Throwable::getMessage) - .isEqualTo("refreshTokenTimeToLive has to be greater than Duration.ZERO"); + .isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO"); } @Test @@ -101,7 +101,7 @@ public class TokenSettingsTests { assertThat(tokenSettings.settings()).hasSize(6); assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive); assertThat(tokenSettings.enableRefreshTokens()).isTrue(); - assertThat(tokenSettings.reuseRefreshTokens()).isFalse(); + assertThat(tokenSettings.reuseRefreshTokens()).isTrue(); assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60)); assertThat(tokenSettings.setting("name1")).isEqualTo("value1"); assertThat(tokenSettings.setting("name2")).isEqualTo("value2"); diff --git a/samples/boot/oauth2-integration/authorizationserver/spring-security-samples-boot-oauth2-integrated-authorizationserver.gradle b/samples/boot/oauth2-integration/authorizationserver/spring-security-samples-boot-oauth2-integrated-authorizationserver.gradle index 2d0bd26..bd40592 100644 --- a/samples/boot/oauth2-integration/authorizationserver/spring-security-samples-boot-oauth2-integrated-authorizationserver.gradle +++ b/samples/boot/oauth2-integration/authorizationserver/spring-security-samples-boot-oauth2-integrated-authorizationserver.gradle @@ -1,7 +1,8 @@ -ext['spring-security.version'] = '5.5.+' // TODO remove once Spring Boot upgraded to Spring Security 5.5 - apply plugin: 'io.spring.convention.spring-sample-boot' +// TODO Remove once Spring Boot upgrades to Spring Security 5.5 +ext['spring-security.version'] = '5.5.+' + dependencies { compile 'org.springframework.boot:spring-boot-starter-web' compile 'org.springframework.boot:spring-boot-starter-security' diff --git a/samples/boot/oauth2-integration/client/src/main/java/sample/config/WebClientConfig.java b/samples/boot/oauth2-integration/client/src/main/java/sample/config/WebClientConfig.java index 443986f..a03d2d1 100644 --- a/samples/boot/oauth2-integration/client/src/main/java/sample/config/WebClientConfig.java +++ b/samples/boot/oauth2-integration/client/src/main/java/sample/config/WebClientConfig.java @@ -50,8 +50,8 @@ public class WebClientConfig { OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() .authorizationCode() - .clientCredentials() .refreshToken() + .clientCredentials() .build(); DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientRepository);