Polish gh-128
This commit is contained in:
parent
78d4bd0bad
commit
a2167a5091
@ -144,16 +144,18 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
|
|||||||
jwtEncoder);
|
jwtEncoder);
|
||||||
builder.authenticationProvider(postProcess(authorizationCodeAuthenticationProvider));
|
builder.authenticationProvider(postProcess(authorizationCodeAuthenticationProvider));
|
||||||
|
|
||||||
|
OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider =
|
||||||
|
new OAuth2RefreshTokenAuthenticationProvider(
|
||||||
|
getAuthorizationService(builder),
|
||||||
|
jwtEncoder);
|
||||||
|
builder.authenticationProvider(postProcess(refreshTokenAuthenticationProvider));
|
||||||
|
|
||||||
OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider =
|
OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider =
|
||||||
new OAuth2ClientCredentialsAuthenticationProvider(
|
new OAuth2ClientCredentialsAuthenticationProvider(
|
||||||
getAuthorizationService(builder),
|
getAuthorizationService(builder),
|
||||||
jwtEncoder);
|
jwtEncoder);
|
||||||
builder.authenticationProvider(postProcess(clientCredentialsAuthenticationProvider));
|
builder.authenticationProvider(postProcess(clientCredentialsAuthenticationProvider));
|
||||||
|
|
||||||
OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider =
|
|
||||||
new OAuth2RefreshTokenAuthenticationProvider(getAuthorizationService(builder), jwtEncoder);
|
|
||||||
builder.authenticationProvider(postProcess(refreshTokenAuthenticationProvider));
|
|
||||||
|
|
||||||
OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider =
|
OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider =
|
||||||
new OAuth2TokenRevocationAuthenticationProvider(
|
new OAuth2TokenRevocationAuthenticationProvider(
|
||||||
getAuthorizationService(builder));
|
getAuthorizationService(builder));
|
||||||
|
@ -66,16 +66,12 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
|||||||
} else if (TokenType.AUTHORIZATION_CODE.equals(tokenType)) {
|
} else if (TokenType.AUTHORIZATION_CODE.equals(tokenType)) {
|
||||||
OAuth2AuthorizationCode authorizationCode = authorization.getTokens().getToken(OAuth2AuthorizationCode.class);
|
OAuth2AuthorizationCode authorizationCode = authorization.getTokens().getToken(OAuth2AuthorizationCode.class);
|
||||||
return authorizationCode != null && authorizationCode.getTokenValue().equals(token);
|
return authorizationCode != null && authorizationCode.getTokenValue().equals(token);
|
||||||
}
|
} else if (TokenType.ACCESS_TOKEN.equals(tokenType)) {
|
||||||
|
|
||||||
if (TokenType.ACCESS_TOKEN.equals(tokenType)) {
|
|
||||||
return authorization.getTokens().getAccessToken() != null &&
|
return authorization.getTokens().getAccessToken() != null &&
|
||||||
authorization.getTokens().getAccessToken().getTokenValue().equals(token);
|
authorization.getTokens().getAccessToken().getTokenValue().equals(token);
|
||||||
}
|
} else if (TokenType.REFRESH_TOKEN.equals(tokenType)) {
|
||||||
|
|
||||||
if (TokenType.REFRESH_TOKEN.equals(tokenType)) {
|
|
||||||
return authorization.getTokens().getRefreshToken() != null &&
|
return authorization.getTokens().getRefreshToken() != null &&
|
||||||
authorization.getTokens().getRefreshToken().getTokenValue().equals(token);
|
authorization.getTokens().getRefreshToken().getTokenValue().equals(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -18,24 +18,25 @@ package org.springframework.security.oauth2.server.authorization.authentication;
|
|||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.oauth2.server.authorization.Version;
|
|
||||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.Version;
|
||||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link Authentication} implementation used when issuing an OAuth 2.0 Access Token.
|
* An {@link Authentication} implementation used when issuing an
|
||||||
|
* OAuth 2.0 Access Token and (optional) Refresh Token.
|
||||||
*
|
*
|
||||||
* @author Joe Grandja
|
* @author Joe Grandja
|
||||||
* @author Madhu Bhat
|
* @author Madhu Bhat
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
* @see AbstractAuthenticationToken
|
* @see AbstractAuthenticationToken
|
||||||
* @see OAuth2AuthorizationCodeAuthenticationProvider
|
|
||||||
* @see RegisteredClient
|
* @see RegisteredClient
|
||||||
* @see OAuth2AccessToken
|
* @see OAuth2AccessToken
|
||||||
|
* @see OAuth2RefreshToken
|
||||||
* @see OAuth2ClientAuthenticationToken
|
* @see OAuth2ClientAuthenticationToken
|
||||||
*/
|
*/
|
||||||
public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken {
|
public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken {
|
||||||
@ -65,8 +66,8 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
|||||||
* @param accessToken the access token
|
* @param accessToken the access token
|
||||||
* @param refreshToken the refresh token
|
* @param refreshToken the refresh token
|
||||||
*/
|
*/
|
||||||
public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient,
|
public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal,
|
||||||
Authentication clientPrincipal, OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {
|
OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {
|
||||||
super(Collections.emptyList());
|
super(Collections.emptyList());
|
||||||
Assert.notNull(registeredClient, "registeredClient cannot be null");
|
Assert.notNull(registeredClient, "registeredClient cannot be null");
|
||||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||||
@ -105,14 +106,13 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
|||||||
return this.accessToken;
|
return this.accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link OAuth2RefreshToken} if provided
|
* Returns the {@link OAuth2RefreshToken refresh token}.
|
||||||
*
|
*
|
||||||
* @return the {@link OAuth2RefreshToken}
|
* @return the {@link OAuth2RefreshToken} or {@code null} if not available
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public OAuth2RefreshToken getRefreshToken() {
|
public OAuth2RefreshToken getRefreshToken() {
|
||||||
return refreshToken;
|
return this.refreshToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,9 +97,9 @@ public class OAuth2ClientCredentialsAuthenticationProvider implements Authentica
|
|||||||
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), scopes);
|
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), scopes);
|
||||||
|
|
||||||
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)
|
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||||
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
|
|
||||||
.principalName(clientPrincipal.getName())
|
.principalName(clientPrincipal.getName())
|
||||||
.tokens(OAuth2Tokens.builder().accessToken(accessToken).build())
|
.tokens(OAuth2Tokens.builder().accessToken(accessToken).build())
|
||||||
|
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
|
||||||
.build();
|
.build();
|
||||||
this.authorizationService.save(authorization);
|
this.authorizationService.save(authorization);
|
||||||
|
|
||||||
|
@ -13,12 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.oauth2.server.authorization.authentication;
|
package org.springframework.security.oauth2.server.authorization.authentication;
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
@ -38,6 +34,9 @@ import org.springframework.security.oauth2.server.authorization.config.TokenSett
|
|||||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
|
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Refresh Token Grant.
|
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Refresh Token Grant.
|
||||||
*
|
*
|
||||||
@ -47,19 +46,23 @@ import org.springframework.util.Assert;
|
|||||||
* @see OAuth2AccessTokenAuthenticationToken
|
* @see OAuth2AccessTokenAuthenticationToken
|
||||||
* @see OAuth2AuthorizationService
|
* @see OAuth2AuthorizationService
|
||||||
* @see JwtEncoder
|
* @see JwtEncoder
|
||||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.5">Section 1.5 Refresh Token</a>
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.5">Section 1.5 Refresh Token Grant</a>
|
||||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-6">Section 6 Refreshing an Access Token</a>
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-6">Section 6 Refreshing an Access Token</a>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider {
|
public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
private final OAuth2AuthorizationService authorizationService;
|
private final OAuth2AuthorizationService authorizationService;
|
||||||
private final JwtEncoder jwtEncoder;
|
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(authorizationService, "authorizationService cannot be null");
|
||||||
Assert.notNull(jwtEncoder, "jwtEncoder cannot be null");
|
Assert.notNull(jwtEncoder, "jwtEncoder cannot be null");
|
||||||
|
|
||||||
this.authorizationService = authorizationService;
|
this.authorizationService = authorizationService;
|
||||||
this.jwtEncoder = jwtEncoder;
|
this.jwtEncoder = jwtEncoder;
|
||||||
}
|
}
|
||||||
@ -73,43 +76,45 @@ public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationP
|
|||||||
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(refreshTokenAuthentication.getPrincipal().getClass())) {
|
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(refreshTokenAuthentication.getPrincipal().getClass())) {
|
||||||
clientPrincipal = (OAuth2ClientAuthenticationToken) refreshTokenAuthentication.getPrincipal();
|
clientPrincipal = (OAuth2ClientAuthenticationToken) refreshTokenAuthentication.getPrincipal();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) {
|
if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) {
|
||||||
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
|
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) {
|
if (authorization == null) {
|
||||||
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
|
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
|
||||||
}
|
}
|
||||||
|
|
||||||
Instant refreshTokenExpiration = authorization.getTokens().getRefreshToken().getExpiresAt();
|
if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {
|
||||||
if (refreshTokenExpiration.isBefore(Instant.now())) {
|
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
|
||||||
// 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 [...].
|
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));
|
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
// As per https://tools.ietf.org/html/rfc6749#section-6
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc6749#section-6
|
|
||||||
// The requested scope MUST NOT include any scope not originally granted by the resource owner,
|
// 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.
|
// and if omitted is treated as equal to the scope originally granted by the resource owner.
|
||||||
Set<String> refreshTokenScopes = refreshTokenAuthentication.getScopes();
|
Set<String> scopes = refreshTokenAuthentication.getScopes();
|
||||||
Set<String> authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES);
|
Set<String> authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES);
|
||||||
if (!authorizedScopes.containsAll(refreshTokenScopes)) {
|
if (!authorizedScopes.containsAll(scopes)) {
|
||||||
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE));
|
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE));
|
||||||
}
|
}
|
||||||
|
if (scopes.isEmpty()) {
|
||||||
if (refreshTokenScopes.isEmpty()) {
|
scopes = authorizedScopes;
|
||||||
refreshTokenScopes = authorizedScopes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Jwt jwt = OAuth2TokenIssuerUtil
|
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,
|
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();
|
TokenSettings tokenSettings = registeredClient.getTokenSettings();
|
||||||
OAuth2RefreshToken refreshToken;
|
OAuth2RefreshToken refreshToken;
|
||||||
@ -120,13 +125,13 @@ public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationP
|
|||||||
}
|
}
|
||||||
|
|
||||||
authorization = OAuth2Authorization.from(authorization)
|
authorization = OAuth2Authorization.from(authorization)
|
||||||
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
|
.tokens(OAuth2Tokens.builder().accessToken(accessToken).refreshToken(refreshToken).build())
|
||||||
.tokens(OAuth2Tokens.builder().accessToken(accessToken).refreshToken(refreshToken).build())
|
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.authorizationService.save(authorization);
|
this.authorizationService.save(authorization);
|
||||||
|
|
||||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken);
|
return new OAuth2AccessTokenAuthenticationToken(
|
||||||
|
registeredClient, clientPrincipal, accessToken, refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -13,16 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.oauth2.server.authorization.authentication;
|
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.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.oauth2.server.authorization.Version;
|
||||||
import org.springframework.util.Assert;
|
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.
|
* An {@link Authentication} implementation used for the OAuth 2.0 Refresh Token Grant.
|
||||||
*
|
*
|
||||||
@ -33,42 +33,37 @@ import org.springframework.util.Assert;
|
|||||||
* @see OAuth2ClientAuthenticationToken
|
* @see OAuth2ClientAuthenticationToken
|
||||||
*/
|
*/
|
||||||
public class OAuth2RefreshTokenAuthenticationToken extends AbstractAuthenticationToken {
|
public class OAuth2RefreshTokenAuthenticationToken extends AbstractAuthenticationToken {
|
||||||
|
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
|
||||||
private final Authentication clientPrincipal;
|
|
||||||
private final String refreshToken;
|
private final String refreshToken;
|
||||||
|
private final Authentication clientPrincipal;
|
||||||
private final Set<String> scopes;
|
private final Set<String> scopes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
|
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
|
||||||
*
|
*
|
||||||
* @param refreshToken refresh token value
|
* @param refreshToken the refresh token
|
||||||
* @param clientPrincipal the authenticated client principal
|
* @param clientPrincipal the authenticated client principal
|
||||||
*/
|
*/
|
||||||
public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal) {
|
public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal) {
|
||||||
this(clientPrincipal, refreshToken, Collections.emptySet());
|
this(refreshToken, clientPrincipal, Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
|
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
|
||||||
*
|
*
|
||||||
|
* @param refreshToken the refresh token
|
||||||
* @param clientPrincipal the authenticated client principal
|
* @param clientPrincipal the authenticated client principal
|
||||||
* @param refreshToken refresh token value
|
* @param scopes the requested scope(s)
|
||||||
* @param requestedScopes scopes requested by refresh token
|
|
||||||
*/
|
*/
|
||||||
public OAuth2RefreshTokenAuthenticationToken(Authentication clientPrincipal, String refreshToken, Set<String> requestedScopes) {
|
public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal,
|
||||||
|
Set<String> scopes) {
|
||||||
super(Collections.emptySet());
|
super(Collections.emptySet());
|
||||||
|
Assert.hasText(refreshToken, "refreshToken cannot be empty");
|
||||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||||
Assert.hasText(refreshToken, "refreshToken cannot be null or empty");
|
Assert.notNull(scopes, "scopes cannot be null");
|
||||||
|
|
||||||
this.clientPrincipal = clientPrincipal;
|
|
||||||
this.refreshToken = refreshToken;
|
this.refreshToken = refreshToken;
|
||||||
this.scopes = requestedScopes;
|
this.clientPrincipal = clientPrincipal;
|
||||||
}
|
this.scopes = scopes;
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCredentials() {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -76,6 +71,16 @@ public class OAuth2RefreshTokenAuthenticationToken extends AbstractAuthenticatio
|
|||||||
return this.clientPrincipal;
|
return this.clientPrincipal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the refresh token.
|
||||||
|
*
|
||||||
|
* @return the refresh token
|
||||||
|
*/
|
||||||
public String getRefreshToken() {
|
public String getRefreshToken() {
|
||||||
return this.refreshToken;
|
return this.refreshToken;
|
||||||
}
|
}
|
||||||
|
@ -13,19 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.security.oauth2.server.authorization.authentication;
|
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.Base64StringKeyGenerator;
|
||||||
import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
||||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
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.JwtClaimsSet;
|
||||||
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
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
|
* @author Alexey Nesterov
|
||||||
* @since 0.0.3
|
* @since 0.0.3
|
||||||
*/
|
*/
|
||||||
class OAuth2TokenIssuerUtil {
|
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<String> scopes) {
|
static Jwt issueJwtAccessToken(JwtEncoder jwtEncoder, String subject, String audience, Set<String> scopes) {
|
||||||
JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build();
|
JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build();
|
||||||
@ -71,8 +70,8 @@ class OAuth2TokenIssuerUtil {
|
|||||||
|
|
||||||
static OAuth2RefreshToken issueRefreshToken(Duration refreshTokenTimeToLive) {
|
static OAuth2RefreshToken issueRefreshToken(Duration refreshTokenTimeToLive) {
|
||||||
Instant issuedAt = Instant.now();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.security.oauth2.server.authorization.config;
|
package org.springframework.security.oauth2.server.authorization.config;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A facility for token configuration settings.
|
* 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
|
* @param accessTokenTimeToLive the time-to-live for an access token
|
||||||
* @return the {@link TokenSettings}
|
* @return the {@link TokenSettings}
|
||||||
*/
|
*/
|
||||||
public TokenSettings accessTokenTimeToLive(Duration accessTokenTimeToLive) {
|
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);
|
setting(ACCESS_TOKEN_TIME_TO_LIVE, accessTokenTimeToLive);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if refresh tokens support is enabled.
|
* Returns {@code true} if refresh tokens are enabled. The default is {@code true}.
|
||||||
* 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}.
|
|
||||||
*
|
*
|
||||||
* @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() {
|
public boolean enableRefreshTokens() {
|
||||||
return setting(ENABLE_REFRESH_TOKENS);
|
return setting(ENABLE_REFRESH_TOKENS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to {@code true} to enable refresh tokens support.
|
* Set to {@code true} to enable refresh tokens.
|
||||||
* This include generation of refresh token as a part of Authorization Code Grant flow and support of Refresh Token
|
|
||||||
* Grant flow.
|
|
||||||
*
|
*
|
||||||
* @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}
|
* @return the {@link TokenSettings}
|
||||||
*/
|
*/
|
||||||
public TokenSettings enableRefreshTokens(boolean enableRefreshTokens) {
|
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,
|
* Returns {@code true} if refresh tokens are reused when returning the access token response,
|
||||||
* or {@code false} if a new refresh token is generated.
|
* or {@code false} if a new refresh token is issued. The default is {@code true}.
|
||||||
* The default is {@code false}.
|
|
||||||
*/
|
*/
|
||||||
public boolean reuseRefreshTokens() {
|
public boolean reuseRefreshTokens() {
|
||||||
return setting(REUSE_REFRESH_TOKENS);
|
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,
|
* Set to {@code true} if refresh tokens are reused when returning the access token response,
|
||||||
* or to {@code false} to generate a new refresh token.
|
* or {@code false} if a new refresh token is issued.
|
||||||
* @param reuseRefreshTokens {@code true} to re-use existing refresh token, {@code false} to generate a new one
|
*
|
||||||
|
* @param reuseRefreshTokens {@code true} to reuse refresh tokens, {@code false} to issue new refresh tokens
|
||||||
|
* @return the {@link TokenSettings}
|
||||||
*/
|
*/
|
||||||
public TokenSettings reuseRefreshTokens(boolean reuseRefreshTokens) {
|
public TokenSettings reuseRefreshTokens(boolean reuseRefreshTokens) {
|
||||||
setting(REUSE_REFRESH_TOKENS, 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}.
|
* Returns the time-to-live for a refresh token. The default is 60 minutes.
|
||||||
* @return refresh token time-to-live
|
*
|
||||||
|
* @return the time-to-live for a refresh token
|
||||||
*/
|
*/
|
||||||
public Duration refreshTokenTimeToLive() {
|
public Duration refreshTokenTimeToLive() {
|
||||||
return setting(REFRESH_TOKEN_TIME_TO_LIVE);
|
return setting(REFRESH_TOKEN_TIME_TO_LIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets refresh token time-to-live.
|
* Set the time-to-live for a refresh token. Must be greater than {@code Duration.ZERO}.
|
||||||
* @param refreshTokenTimeToLive refresh token time-to-live. Has to 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) {
|
public TokenSettings refreshTokenTimeToLive(Duration refreshTokenTimeToLive) {
|
||||||
Assert.notNull(refreshTokenTimeToLive, "refreshTokenTimeToLive cannot be null");
|
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);
|
setting(REFRESH_TOKEN_TIME_TO_LIVE, refreshTokenTimeToLive);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -138,7 +139,7 @@ public class TokenSettings extends Settings {
|
|||||||
Map<String, Object> settings = new HashMap<>();
|
Map<String, Object> settings = new HashMap<>();
|
||||||
settings.put(ACCESS_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(5));
|
settings.put(ACCESS_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(5));
|
||||||
settings.put(ENABLE_REFRESH_TOKENS, true);
|
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));
|
settings.put(REFRESH_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(60));
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
@ -130,8 +130,8 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
|
|||||||
this.tokenEndpointMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name());
|
this.tokenEndpointMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name());
|
||||||
Map<AuthorizationGrantType, Converter<HttpServletRequest, Authentication>> converters = new HashMap<>();
|
Map<AuthorizationGrantType, Converter<HttpServletRequest, Authentication>> converters = new HashMap<>();
|
||||||
converters.put(AuthorizationGrantType.AUTHORIZATION_CODE, new AuthorizationCodeAuthenticationConverter());
|
converters.put(AuthorizationGrantType.AUTHORIZATION_CODE, new AuthorizationCodeAuthenticationConverter());
|
||||||
converters.put(AuthorizationGrantType.CLIENT_CREDENTIALS, new ClientCredentialsAuthenticationConverter());
|
|
||||||
converters.put(AuthorizationGrantType.REFRESH_TOKEN, new RefreshTokenAuthenticationConverter());
|
converters.put(AuthorizationGrantType.REFRESH_TOKEN, new RefreshTokenAuthenticationConverter());
|
||||||
|
converters.put(AuthorizationGrantType.CLIENT_CREDENTIALS, new ClientCredentialsAuthenticationConverter());
|
||||||
this.authorizationGrantAuthenticationConverter = new DelegatingAuthorizationGrantAuthenticationConverter(converters);
|
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.Builder builder =
|
||||||
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
|
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
|
||||||
.tokenType(accessToken.getTokenType())
|
.tokenType(accessToken.getTokenType())
|
||||||
@ -235,6 +237,43 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class RefreshTokenAuthenticationConverter implements Converter<HttpServletRequest, Authentication> {
|
||||||
|
|
||||||
|
@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<String, String> 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<String> 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<HttpServletRequest, Authentication> {
|
private static class ClientCredentialsAuthenticationConverter implements Converter<HttpServletRequest, Authentication> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -264,41 +303,4 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
|
|||||||
return new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
|
return new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RefreshTokenAuthenticationConverter implements Converter<HttpServletRequest, Authentication> {
|
|
||||||
|
|
||||||
@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<String, String> 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<String> requestedScopes = new HashSet<>(
|
|
||||||
Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
|
|
||||||
return new OAuth2RefreshTokenAuthenticationToken(clientPrincipal, refreshToken, requestedScopes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests {
|
|||||||
when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN))
|
when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN))
|
||||||
.thenReturn(this.authorization);
|
.thenReturn(this.authorization);
|
||||||
|
|
||||||
RegisteredClient clientWithReuseTokensTrue = TestRegisteredClients.registeredClient()
|
RegisteredClient clientWithReuseTokensTrue = TestRegisteredClients.registeredClient2()
|
||||||
.tokenSettings(tokenSettings -> tokenSettings.reuseRefreshTokens(true))
|
.tokenSettings(tokenSettings -> tokenSettings.reuseRefreshTokens(true))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests {
|
|||||||
when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN))
|
when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN))
|
||||||
.thenReturn(this.authorization);
|
.thenReturn(this.authorization);
|
||||||
|
|
||||||
RegisteredClient clientWithReuseTokensFalse = TestRegisteredClients.registeredClient()
|
RegisteredClient clientWithReuseTokensFalse = TestRegisteredClients.registeredClient2()
|
||||||
.tokenSettings(tokenSettings -> tokenSettings.reuseRefreshTokens(false))
|
.tokenSettings(tokenSettings -> tokenSettings.reuseRefreshTokens(false))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests {
|
|||||||
requestedScopes.add("openid");
|
requestedScopes.add("openid");
|
||||||
|
|
||||||
OAuth2RefreshTokenAuthenticationToken tokenWithScopes
|
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))
|
when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN))
|
||||||
.thenReturn(this.authorization);
|
.thenReturn(this.authorization);
|
||||||
@ -227,7 +227,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests {
|
|||||||
requestedScopes.add("another-scope");
|
requestedScopes.add("another-scope");
|
||||||
|
|
||||||
OAuth2RefreshTokenAuthenticationToken tokenWithScopes
|
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))
|
when(this.authorizationService.findByToken(REFRESH_TOKEN_VALUE, TokenType.REFRESH_TOKEN))
|
||||||
.thenReturn(this.authorization);
|
.thenReturn(this.authorization);
|
||||||
|
@ -34,7 +34,7 @@ public class OAuth2RefreshTokenAuthenticationTokenTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenClientPrincipalNullThrowException() {
|
public void constructorWhenClientPrincipalNullThrowException() {
|
||||||
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", null))
|
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("test", null))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage("clientPrincipal cannot be null");
|
.hasMessage("clientPrincipal cannot be null");
|
||||||
}
|
}
|
||||||
@ -43,18 +43,18 @@ public class OAuth2RefreshTokenAuthenticationTokenTests {
|
|||||||
public void constructorWhenRefreshTokenNullOrEmptyThrowException() {
|
public void constructorWhenRefreshTokenNullOrEmptyThrowException() {
|
||||||
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, mock(OAuth2ClientAuthenticationToken.class)))
|
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, mock(OAuth2ClientAuthenticationToken.class)))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage("refreshToken cannot be null or empty");
|
.hasMessage("refreshToken cannot be empty");
|
||||||
|
|
||||||
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", mock(OAuth2ClientAuthenticationToken.class)))
|
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", mock(OAuth2ClientAuthenticationToken.class)))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage("refreshToken cannot be null or empty");
|
.hasMessage("refreshToken cannot be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenGettingScopesThenReturnRequestedScopes() {
|
public void constructorWhenGettingScopesThenReturnRequestedScopes() {
|
||||||
Set<String> expectedScopes = new HashSet<>(Arrays.asList("scope-a", "scope-b"));
|
Set<String> expectedScopes = new HashSet<>(Arrays.asList("scope-a", "scope-b"));
|
||||||
OAuth2RefreshTokenAuthenticationToken token
|
OAuth2RefreshTokenAuthenticationToken token
|
||||||
= new OAuth2RefreshTokenAuthenticationToken(mock(OAuth2ClientAuthenticationToken.class), "test", expectedScopes);
|
= new OAuth2RefreshTokenAuthenticationToken("test", mock(OAuth2ClientAuthenticationToken.class), expectedScopes);
|
||||||
|
|
||||||
assertThat(token.getScopes()).containsAll(expectedScopes);
|
assertThat(token.getScopes()).containsAll(expectedScopes);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ public class TokenSettingsTests {
|
|||||||
assertThat(tokenSettings.settings()).hasSize(4);
|
assertThat(tokenSettings.settings()).hasSize(4);
|
||||||
assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
|
assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
|
||||||
assertThat(tokenSettings.enableRefreshTokens()).isTrue();
|
assertThat(tokenSettings.enableRefreshTokens()).isTrue();
|
||||||
assertThat(tokenSettings.reuseRefreshTokens()).isEqualTo(false);
|
assertThat(tokenSettings.reuseRefreshTokens()).isEqualTo(true);
|
||||||
assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
|
assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,12 +83,12 @@ public class TokenSettingsTests {
|
|||||||
assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(Duration.ZERO))
|
assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(Duration.ZERO))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.extracting(Throwable::getMessage)
|
.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)))
|
assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(Duration.ofSeconds(-10)))
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.extracting(Throwable::getMessage)
|
.extracting(Throwable::getMessage)
|
||||||
.isEqualTo("refreshTokenTimeToLive has to be greater than Duration.ZERO");
|
.isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -101,7 +101,7 @@ public class TokenSettingsTests {
|
|||||||
assertThat(tokenSettings.settings()).hasSize(6);
|
assertThat(tokenSettings.settings()).hasSize(6);
|
||||||
assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive);
|
assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive);
|
||||||
assertThat(tokenSettings.enableRefreshTokens()).isTrue();
|
assertThat(tokenSettings.enableRefreshTokens()).isTrue();
|
||||||
assertThat(tokenSettings.reuseRefreshTokens()).isFalse();
|
assertThat(tokenSettings.reuseRefreshTokens()).isTrue();
|
||||||
assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
|
assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
|
||||||
assertThat(tokenSettings.<String>setting("name1")).isEqualTo("value1");
|
assertThat(tokenSettings.<String>setting("name1")).isEqualTo("value1");
|
||||||
assertThat(tokenSettings.<String>setting("name2")).isEqualTo("value2");
|
assertThat(tokenSettings.<String>setting("name2")).isEqualTo("value2");
|
||||||
|
@ -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'
|
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 {
|
dependencies {
|
||||||
compile 'org.springframework.boot:spring-boot-starter-web'
|
compile 'org.springframework.boot:spring-boot-starter-web'
|
||||||
compile 'org.springframework.boot:spring-boot-starter-security'
|
compile 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
@ -50,8 +50,8 @@ public class WebClientConfig {
|
|||||||
OAuth2AuthorizedClientProvider authorizedClientProvider =
|
OAuth2AuthorizedClientProvider authorizedClientProvider =
|
||||||
OAuth2AuthorizedClientProviderBuilder.builder()
|
OAuth2AuthorizedClientProviderBuilder.builder()
|
||||||
.authorizationCode()
|
.authorizationCode()
|
||||||
.clientCredentials()
|
|
||||||
.refreshToken()
|
.refreshToken()
|
||||||
|
.clientCredentials()
|
||||||
.build();
|
.build();
|
||||||
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
|
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
|
||||||
clientRegistrationRepository, authorizedClientRepository);
|
clientRegistrationRepository, authorizedClientRepository);
|
||||||
|
Loading…
Reference in New Issue
Block a user