Associate Refresh Token to OAuth2AuthorizedClient
Fixes gh-5416
This commit is contained in:
parent
1137f3b46d
commit
02d29887fb
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -15,8 +15,10 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.client;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -33,11 +35,13 @@ import org.springframework.util.Assert;
|
|||
* @since 5.0
|
||||
* @see ClientRegistration
|
||||
* @see OAuth2AccessToken
|
||||
* @see OAuth2RefreshToken
|
||||
*/
|
||||
public class OAuth2AuthorizedClient {
|
||||
private final ClientRegistration clientRegistration;
|
||||
private final String principalName;
|
||||
private final OAuth2AccessToken accessToken;
|
||||
private final OAuth2RefreshToken refreshToken;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizedClient} using the provided parameters.
|
||||
|
@ -47,12 +51,26 @@ public class OAuth2AuthorizedClient {
|
|||
* @param accessToken the access token credential granted
|
||||
*/
|
||||
public OAuth2AuthorizedClient(ClientRegistration clientRegistration, String principalName, OAuth2AccessToken accessToken) {
|
||||
this(clientRegistration, principalName, accessToken, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizedClient} using the provided parameters.
|
||||
*
|
||||
* @param clientRegistration the authorized client's registration
|
||||
* @param principalName the name of the End-User {@code Principal} (Resource Owner)
|
||||
* @param accessToken the access token credential granted
|
||||
* @param refreshToken the refresh token credential granted
|
||||
*/
|
||||
public OAuth2AuthorizedClient(ClientRegistration clientRegistration, String principalName,
|
||||
OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {
|
||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||
Assert.hasText(principalName, "principalName cannot be empty");
|
||||
Assert.notNull(accessToken, "accessToken cannot be null");
|
||||
this.clientRegistration = clientRegistration;
|
||||
this.principalName = principalName;
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,4 +99,14 @@ public class OAuth2AuthorizedClient {
|
|||
public OAuth2AccessToken getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2RefreshToken refresh token} credential granted.
|
||||
*
|
||||
* @since 5.1
|
||||
* @return the {@link OAuth2RefreshToken}
|
||||
*/
|
||||
public @Nullable OAuth2RefreshToken getRefreshToken() {
|
||||
return this.refreshToken;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -69,13 +68,12 @@ public class OAuth2AuthorizationCodeAuthenticationProvider implements Authentica
|
|||
authorizationCodeAuthentication.getClientRegistration(),
|
||||
authorizationCodeAuthentication.getAuthorizationExchange()));
|
||||
|
||||
OAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();
|
||||
|
||||
OAuth2AuthorizationCodeAuthenticationToken authenticationResult =
|
||||
new OAuth2AuthorizationCodeAuthenticationToken(
|
||||
authorizationCodeAuthentication.getClientRegistration(),
|
||||
authorizationCodeAuthentication.getAuthorizationExchange(),
|
||||
accessToken);
|
||||
accessTokenResponse.getAccessToken(),
|
||||
accessTokenResponse.getRefreshToken());
|
||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
|
|
|
@ -15,10 +15,12 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.client.authentication;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
@ -40,6 +42,7 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractAuthenti
|
|||
private ClientRegistration clientRegistration;
|
||||
private OAuth2AuthorizationExchange authorizationExchange;
|
||||
private OAuth2AccessToken accessToken;
|
||||
private OAuth2RefreshToken refreshToken;
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Authorization Request/Response is complete.
|
||||
|
@ -67,9 +70,26 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractAuthenti
|
|||
public OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange,
|
||||
OAuth2AccessToken accessToken) {
|
||||
this(clientRegistration, authorizationExchange, accessToken, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Access Token Request/Response is complete,
|
||||
* which indicates that the Authorization Code Grant flow has fully completed.
|
||||
*
|
||||
* @param clientRegistration the client registration
|
||||
* @param authorizationExchange the authorization exchange
|
||||
* @param accessToken the access token credential
|
||||
* @param refreshToken the refresh token credential
|
||||
*/
|
||||
public OAuth2AuthorizationCodeAuthenticationToken(ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange,
|
||||
OAuth2AccessToken accessToken,
|
||||
@Nullable OAuth2RefreshToken refreshToken) {
|
||||
this(clientRegistration, authorizationExchange);
|
||||
Assert.notNull(accessToken, "accessToken cannot be null");
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
this.setAuthenticated(true);
|
||||
}
|
||||
|
||||
|
@ -111,4 +131,13 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractAuthenti
|
|||
public OAuth2AccessToken getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2RefreshToken refresh token}.
|
||||
*
|
||||
* @return the {@link OAuth2RefreshToken}
|
||||
*/
|
||||
public @Nullable OAuth2RefreshToken getRefreshToken() {
|
||||
return this.refreshToken;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -113,7 +113,8 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||
authorizationCodeAuthentication.getAuthorizationExchange(),
|
||||
oauth2User,
|
||||
mappedAuthorities,
|
||||
accessToken);
|
||||
accessToken,
|
||||
accessTokenResponse.getRefreshToken());
|
||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -15,11 +15,13 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.client.authentication;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -46,6 +48,7 @@ public class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken
|
|||
private ClientRegistration clientRegistration;
|
||||
private OAuth2AuthorizationExchange authorizationExchange;
|
||||
private OAuth2AccessToken accessToken;
|
||||
private OAuth2RefreshToken refreshToken;
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Authorization Request/Response is complete.
|
||||
|
@ -80,6 +83,27 @@ public class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken
|
|||
OAuth2User principal,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
OAuth2AccessToken accessToken) {
|
||||
this(clientRegistration, authorizationExchange, principal, authorities, accessToken, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor should be used when the Access Token Request/Response is complete,
|
||||
* which indicates that the Authorization Code Grant flow has fully completed
|
||||
* and OAuth 2.0 Login has been achieved.
|
||||
*
|
||||
* @param clientRegistration the client registration
|
||||
* @param authorizationExchange the authorization exchange
|
||||
* @param principal the user {@code Principal} registered with the OAuth 2.0 Provider
|
||||
* @param authorities the authorities granted to the user
|
||||
* @param accessToken the access token credential
|
||||
* @param refreshToken the refresh token credential
|
||||
*/
|
||||
public OAuth2LoginAuthenticationToken(ClientRegistration clientRegistration,
|
||||
OAuth2AuthorizationExchange authorizationExchange,
|
||||
OAuth2User principal,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
OAuth2AccessToken accessToken,
|
||||
@Nullable OAuth2RefreshToken refreshToken) {
|
||||
super(authorities);
|
||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||
Assert.notNull(authorizationExchange, "authorizationExchange cannot be null");
|
||||
|
@ -89,6 +113,7 @@ public class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken
|
|||
this.authorizationExchange = authorizationExchange;
|
||||
this.principal = principal;
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
this.setAuthenticated(true);
|
||||
}
|
||||
|
||||
|
@ -128,4 +153,14 @@ public class OAuth2LoginAuthenticationToken extends AbstractAuthenticationToken
|
|||
public OAuth2AccessToken getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2RefreshToken refresh token}.
|
||||
*
|
||||
* @since 5.1
|
||||
* @return the {@link OAuth2RefreshToken}
|
||||
*/
|
||||
public @Nullable OAuth2RefreshToken getRefreshToken() {
|
||||
return this.refreshToken;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,11 +120,13 @@ public class OAuth2LoginReactiveAuthenticationManager implements
|
|||
authorizationCodeAuthentication.getAuthorizationExchange(),
|
||||
oauth2User,
|
||||
mappedAuthorities,
|
||||
accessToken);
|
||||
accessToken,
|
||||
accessTokenResponse.getRefreshToken());
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||
authenticationResult.getClientRegistration(),
|
||||
authenticationResult.getName(),
|
||||
authenticationResult.getAccessToken());
|
||||
authenticationResult.getAccessToken(),
|
||||
authenticationResult.getRefreshToken());
|
||||
OAuth2AuthenticationToken result = new OAuth2AuthenticationToken(
|
||||
authenticationResult.getPrincipal(),
|
||||
authenticationResult.getAuthorities(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -138,12 +138,18 @@ public class NimbusAuthorizationCodeTokenResponseClient implements OAuth2AccessT
|
|||
accessTokenResponse.getTokens().getAccessToken().getScope().toStringList());
|
||||
}
|
||||
|
||||
String refreshToken = null;
|
||||
if (accessTokenResponse.getTokens().getRefreshToken() != null) {
|
||||
refreshToken = accessTokenResponse.getTokens().getRefreshToken().getValue();
|
||||
}
|
||||
|
||||
Map<String, Object> additionalParameters = new LinkedHashMap<>(accessTokenResponse.getCustomParameters());
|
||||
|
||||
return OAuth2AccessTokenResponse.withToken(accessToken)
|
||||
.tokenType(accessTokenType)
|
||||
.expiresIn(expiresIn)
|
||||
.scopes(scopes)
|
||||
.refreshToken(refreshToken)
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -118,6 +118,11 @@ public class NimbusReactiveAuthorizationCodeTokenResponseClient implements React
|
|||
accessToken.getScope().toStringList());
|
||||
}
|
||||
|
||||
String refreshToken = null;
|
||||
if (accessTokenResponse.getTokens().getRefreshToken() != null) {
|
||||
refreshToken = accessTokenResponse.getTokens().getRefreshToken().getValue();
|
||||
}
|
||||
|
||||
Map<String, Object> additionalParameters = new LinkedHashMap<>(
|
||||
accessTokenResponse.getCustomParameters());
|
||||
|
||||
|
@ -125,6 +130,7 @@ public class NimbusReactiveAuthorizationCodeTokenResponseClient implements React
|
|||
.tokenType(accessTokenType)
|
||||
.expiresIn(expiresIn)
|
||||
.scopes(scopes)
|
||||
.refreshToken(refreshToken)
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -27,7 +27,6 @@ import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
|||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
|
@ -142,8 +141,6 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
authorizationCodeAuthentication.getClientRegistration(),
|
||||
authorizationCodeAuthentication.getAuthorizationExchange()));
|
||||
|
||||
OAuth2AccessToken accessToken = accessTokenResponse.getAccessToken();
|
||||
|
||||
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
|
||||
|
||||
if (!accessTokenResponse.getAdditionalParameters().containsKey(OidcParameterNames.ID_TOKEN)) {
|
||||
|
@ -161,7 +158,7 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
this.validateIdToken(idToken, clientRegistration);
|
||||
|
||||
OidcUser oidcUser = this.userService.loadUser(
|
||||
new OidcUserRequest(clientRegistration, accessToken, idToken));
|
||||
new OidcUserRequest(clientRegistration, accessTokenResponse.getAccessToken(), idToken));
|
||||
|
||||
Collection<? extends GrantedAuthority> mappedAuthorities =
|
||||
this.authoritiesMapper.mapAuthorities(oidcUser.getAuthorities());
|
||||
|
@ -171,7 +168,8 @@ public class OidcAuthorizationCodeAuthenticationProvider implements Authenticati
|
|||
authorizationCodeAuthentication.getAuthorizationExchange(),
|
||||
oidcUser,
|
||||
mappedAuthorities,
|
||||
accessToken);
|
||||
accessTokenResponse.getAccessToken(),
|
||||
accessTokenResponse.getRefreshToken());
|
||||
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
|
||||
|
||||
return authenticationResult;
|
||||
|
|
|
@ -198,7 +198,8 @@ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter {
|
|||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||
authenticationResult.getClientRegistration(),
|
||||
currentAuthentication.getName(),
|
||||
authenticationResult.getAccessToken());
|
||||
authenticationResult.getAccessToken(),
|
||||
authenticationResult.getRefreshToken());
|
||||
|
||||
this.authorizedClientService.saveAuthorizedClient(authorizedClient, currentAuthentication);
|
||||
|
||||
|
|
|
@ -173,7 +173,8 @@ public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProce
|
|||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
|
||||
authenticationResult.getClientRegistration(),
|
||||
oauth2Authentication.getName(),
|
||||
authenticationResult.getAccessToken());
|
||||
authenticationResult.getAccessToken(),
|
||||
authenticationResult.getRefreshToken());
|
||||
|
||||
this.authorizedClientService.saveAuthorizedClient(authorizedClient, oauth2Authentication);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
|
@ -122,8 +123,10 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
|
|||
@Test
|
||||
public void authenticateWhenAuthorizationSuccessResponseThenExchangedForAccessToken() {
|
||||
OAuth2AccessToken accessToken = mock(OAuth2AccessToken.class);
|
||||
OAuth2RefreshToken refreshToken = mock(OAuth2RefreshToken.class);
|
||||
OAuth2AccessTokenResponse accessTokenResponse = mock(OAuth2AccessTokenResponse.class);
|
||||
when(accessTokenResponse.getAccessToken()).thenReturn(accessToken);
|
||||
when(accessTokenResponse.getRefreshToken()).thenReturn(refreshToken);
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2AuthorizationCodeAuthenticationToken authenticationResult =
|
||||
|
@ -137,5 +140,6 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
|
|||
assertThat(authenticationResult.getClientRegistration()).isEqualTo(this.clientRegistration);
|
||||
assertThat(authenticationResult.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);
|
||||
assertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken);
|
||||
assertThat(authenticationResult.getRefreshToken()).isEqualTo(refreshToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -35,6 +35,7 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
|
@ -164,8 +165,10 @@ public class OAuth2LoginAuthenticationProviderTests {
|
|||
@Test
|
||||
public void authenticateWhenLoginSuccessThenReturnAuthentication() {
|
||||
OAuth2AccessToken accessToken = mock(OAuth2AccessToken.class);
|
||||
OAuth2RefreshToken refreshToken = mock(OAuth2RefreshToken.class);
|
||||
OAuth2AccessTokenResponse accessTokenResponse = mock(OAuth2AccessTokenResponse.class);
|
||||
when(accessTokenResponse.getAccessToken()).thenReturn(accessToken);
|
||||
when(accessTokenResponse.getRefreshToken()).thenReturn(refreshToken);
|
||||
when(this.accessTokenResponseClient.getTokenResponse(any())).thenReturn(accessTokenResponse);
|
||||
|
||||
OAuth2User principal = mock(OAuth2User.class);
|
||||
|
@ -185,6 +188,7 @@ public class OAuth2LoginAuthenticationProviderTests {
|
|||
assertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);
|
||||
assertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);
|
||||
assertThat(authentication.getAccessToken()).isEqualTo(accessToken);
|
||||
assertThat(authentication.getRefreshToken()).isEqualTo(refreshToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -91,6 +91,7 @@ public class NimbusAuthorizationCodeTokenResponseClientTests {
|
|||
" \"token_type\": \"bearer\",\n" +
|
||||
" \"expires_in\": \"3600\",\n" +
|
||||
" \"scope\": \"openid profile\",\n" +
|
||||
" \"refresh_token\": \"refresh-token-1234\",\n" +
|
||||
" \"custom_parameter_1\": \"custom-value-1\",\n" +
|
||||
" \"custom_parameter_2\": \"custom-value-2\"\n" +
|
||||
"}\n";
|
||||
|
@ -115,6 +116,7 @@ public class NimbusAuthorizationCodeTokenResponseClientTests {
|
|||
assertThat(accessTokenResponse.getAccessToken().getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER);
|
||||
assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);
|
||||
assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile");
|
||||
assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234");
|
||||
assertThat(accessTokenResponse.getAdditionalParameters().size()).isEqualTo(2);
|
||||
assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_1", "custom-value-1");
|
||||
assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_2", "custom-value-2");
|
||||
|
|
|
@ -85,6 +85,7 @@ public class NimbusReactiveAuthorizationCodeTokenResponseClientTests {
|
|||
" \"token_type\": \"bearer\",\n" +
|
||||
" \"expires_in\": \"3600\",\n" +
|
||||
" \"scope\": \"openid profile\",\n" +
|
||||
" \"refresh_token\": \"refresh-token-1234\",\n" +
|
||||
" \"custom_parameter_1\": \"custom-value-1\",\n" +
|
||||
" \"custom_parameter_2\": \"custom-value-2\"\n" +
|
||||
"}\n";
|
||||
|
@ -102,6 +103,7 @@ public class NimbusReactiveAuthorizationCodeTokenResponseClientTests {
|
|||
OAuth2AccessToken.TokenType.BEARER);
|
||||
assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter);
|
||||
assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile");
|
||||
assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234");
|
||||
assertThat(accessTokenResponse.getAdditionalParameters().size()).isEqualTo(2);
|
||||
assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_1", "custom-value-1");
|
||||
assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_2", "custom-value-2");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -37,6 +37,7 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
|
@ -78,6 +79,7 @@ public class OidcAuthorizationCodeAuthenticationProviderTests {
|
|||
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient;
|
||||
private OAuth2AccessTokenResponse accessTokenResponse;
|
||||
private OAuth2AccessToken accessToken;
|
||||
private OAuth2RefreshToken refreshToken;
|
||||
private OAuth2UserService<OidcUserRequest, OidcUser> userService;
|
||||
private OidcAuthorizationCodeAuthenticationProvider authenticationProvider;
|
||||
|
||||
|
@ -95,6 +97,7 @@ public class OidcAuthorizationCodeAuthenticationProviderTests {
|
|||
this.accessTokenResponseClient = mock(OAuth2AccessTokenResponseClient.class);
|
||||
this.accessTokenResponse = mock(OAuth2AccessTokenResponse.class);
|
||||
this.accessToken = mock(OAuth2AccessToken.class);
|
||||
this.refreshToken = mock(OAuth2RefreshToken.class);
|
||||
this.userService = mock(OAuth2UserService.class);
|
||||
this.authenticationProvider = PowerMockito.spy(
|
||||
new OidcAuthorizationCodeAuthenticationProvider(this.accessTokenResponseClient, this.userService));
|
||||
|
@ -109,6 +112,7 @@ public class OidcAuthorizationCodeAuthenticationProviderTests {
|
|||
when(this.authorizationRequest.getRedirectUri()).thenReturn("http://example.com");
|
||||
when(this.authorizationResponse.getRedirectUri()).thenReturn("http://example.com");
|
||||
when(this.accessTokenResponse.getAccessToken()).thenReturn(this.accessToken);
|
||||
when(this.accessTokenResponse.getRefreshToken()).thenReturn(this.refreshToken);
|
||||
Map<String, Object> additionalParameters = new HashMap<>();
|
||||
additionalParameters.put(OidcParameterNames.ID_TOKEN, "id-token");
|
||||
when(this.accessTokenResponse.getAdditionalParameters()).thenReturn(additionalParameters);
|
||||
|
@ -365,6 +369,7 @@ public class OidcAuthorizationCodeAuthenticationProviderTests {
|
|||
assertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);
|
||||
assertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);
|
||||
assertThat(authentication.getAccessToken()).isEqualTo(this.accessToken);
|
||||
assertThat(authentication.getRefreshToken()).isEqualTo(this.refreshToken);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
|
@ -238,6 +239,7 @@ public class OAuth2AuthorizationCodeGrantFilterTests {
|
|||
assertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1);
|
||||
assertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principalName1);
|
||||
assertThat(authorizedClient.getAccessToken()).isNotNull();
|
||||
assertThat(authorizedClient.getRefreshToken()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -299,6 +301,7 @@ public class OAuth2AuthorizationCodeGrantFilterTests {
|
|||
when(authentication.getClientRegistration()).thenReturn(registration);
|
||||
when(authentication.getAuthorizationExchange()).thenReturn(mock(OAuth2AuthorizationExchange.class));
|
||||
when(authentication.getAccessToken()).thenReturn(mock(OAuth2AccessToken.class));
|
||||
when(authentication.getRefreshToken()).thenReturn(mock(OAuth2RefreshToken.class));
|
||||
when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(authentication);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -40,6 +40,7 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
|||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
|
@ -281,6 +282,7 @@ public class OAuth2LoginAuthenticationFilterTests {
|
|||
assertThat(authorizedClient.getClientRegistration()).isEqualTo(this.registration1);
|
||||
assertThat(authorizedClient.getPrincipalName()).isEqualTo(this.principalName1);
|
||||
assertThat(authorizedClient.getAccessToken()).isNotNull();
|
||||
assertThat(authorizedClient.getRefreshToken()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -328,6 +330,7 @@ public class OAuth2LoginAuthenticationFilterTests {
|
|||
when(loginAuthentication.getClientRegistration()).thenReturn(registration);
|
||||
when(loginAuthentication.getAuthorizationExchange()).thenReturn(mock(OAuth2AuthorizationExchange.class));
|
||||
when(loginAuthentication.getAccessToken()).thenReturn(mock(OAuth2AccessToken.class));
|
||||
when(loginAuthentication.getRefreshToken()).thenReturn(mock(OAuth2RefreshToken.class));
|
||||
when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(loginAuthentication);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -37,6 +37,7 @@ public final class AuthorizationGrantType implements Serializable {
|
|||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
public static final AuthorizationGrantType AUTHORIZATION_CODE = new AuthorizationGrantType("authorization_code");
|
||||
public static final AuthorizationGrantType IMPLICIT = new AuthorizationGrantType("implicit");
|
||||
public static final AuthorizationGrantType REFRESH_TOKEN = new AuthorizationGrantType("refresh_token");
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.core;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link AbstractOAuth2Token} representing an OAuth 2.0 Refresh Token.
|
||||
*
|
||||
* <p>
|
||||
* A refresh token is a credential that represents an authorization
|
||||
* granted by the resource owner to the client.
|
||||
* It is used by the client to obtain a new access token when the current access token
|
||||
* becomes invalid or expires, or to obtain additional access tokens with identical or narrower scope.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.1
|
||||
* @see OAuth2AccessToken
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.5">Section 1.5 Refresh Token</a>
|
||||
*/
|
||||
public class OAuth2RefreshToken extends AbstractOAuth2Token {
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2RefreshToken} using the provided parameters.
|
||||
*
|
||||
* @param tokenValue the token value
|
||||
* @param issuedAt the time at which the token was issued
|
||||
* @param expiresAt the expiration time on or after which the token MUST NOT be accepted
|
||||
*/
|
||||
public OAuth2RefreshToken(String tokenValue, Instant issuedAt, Instant expiresAt) {
|
||||
super(tokenValue, issuedAt, expiresAt);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -15,8 +15,11 @@
|
|||
*/
|
||||
package org.springframework.security.oauth2.core.endpoint;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
|
@ -29,10 +32,12 @@ import java.util.Set;
|
|||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2AccessToken
|
||||
* @see OAuth2RefreshToken
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-5.1">Section 5.1 Access Token Response</a>
|
||||
*/
|
||||
public final class OAuth2AccessTokenResponse {
|
||||
private OAuth2AccessToken accessToken;
|
||||
private OAuth2RefreshToken refreshToken;
|
||||
private Map<String, Object> additionalParameters;
|
||||
|
||||
private OAuth2AccessTokenResponse() {
|
||||
|
@ -47,6 +52,16 @@ public final class OAuth2AccessTokenResponse {
|
|||
return this.accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2RefreshToken Refresh Token}.
|
||||
*
|
||||
* @since 5.1
|
||||
* @return the {@link OAuth2RefreshToken}
|
||||
*/
|
||||
public @Nullable OAuth2RefreshToken getRefreshToken() {
|
||||
return this.refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the additional parameters returned in the response.
|
||||
*
|
||||
|
@ -74,6 +89,7 @@ public final class OAuth2AccessTokenResponse {
|
|||
private OAuth2AccessToken.TokenType tokenType;
|
||||
private long expiresIn;
|
||||
private Set<String> scopes;
|
||||
private String refreshToken;
|
||||
private Map<String, Object> additionalParameters;
|
||||
|
||||
private Builder(String tokenValue) {
|
||||
|
@ -113,6 +129,17 @@ public final class OAuth2AccessTokenResponse {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the refresh token associated to the access token.
|
||||
*
|
||||
* @param refreshToken the refresh token associated to the access token.
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder refreshToken(String refreshToken) {
|
||||
this.refreshToken = refreshToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the additional parameters returned in the response.
|
||||
*
|
||||
|
@ -142,6 +169,14 @@ public final class OAuth2AccessTokenResponse {
|
|||
OAuth2AccessTokenResponse accessTokenResponse = new OAuth2AccessTokenResponse();
|
||||
accessTokenResponse.accessToken = new OAuth2AccessToken(
|
||||
this.tokenType, this.tokenValue, issuedAt, expiresAt, this.scopes);
|
||||
if (StringUtils.hasText(this.refreshToken)) {
|
||||
// The Access Token response does not return an expires_in for the Refresh Token,
|
||||
// therefore, we'll default to +1 second from issuedAt time.
|
||||
// NOTE:
|
||||
// The expiry or invalidity of a Refresh Token can only be determined by performing
|
||||
// the refresh_token grant and if it fails than likely it has expired or has been invalidated.
|
||||
accessTokenResponse.refreshToken = new OAuth2RefreshToken(this.refreshToken, issuedAt, issuedAt.plusSeconds(1));
|
||||
}
|
||||
accessTokenResponse.additionalParameters = Collections.unmodifiableMap(
|
||||
CollectionUtils.isEmpty(this.additionalParameters) ? Collections.emptyMap() : this.additionalParameters);
|
||||
return accessTokenResponse;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -40,4 +40,9 @@ public class AuthorizationGrantTypeTests {
|
|||
public void getValueWhenImplicitGrantTypeThenReturnImplicit() {
|
||||
assertThat(AuthorizationGrantType.IMPLICIT.getValue()).isEqualTo("implicit");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueWhenRefreshTokenGrantTypeThenReturnRefreshToken() {
|
||||
assertThat(AuthorizationGrantType.REFRESH_TOKEN.getValue()).isEqualTo("refresh_token");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
public class OAuth2AccessTokenResponseTests {
|
||||
private static final String TOKEN_VALUE = "access-token";
|
||||
private static final String REFRESH_TOKEN_VALUE = "refresh-token";
|
||||
private static final long EXPIRES_IN = Instant.now().plusSeconds(5).toEpochMilli();
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
|
@ -88,6 +89,7 @@ public class OAuth2AccessTokenResponseTests {
|
|||
.tokenType(OAuth2AccessToken.TokenType.BEARER)
|
||||
.expiresIn(expiresAt.toEpochMilli())
|
||||
.scopes(scopes)
|
||||
.refreshToken(REFRESH_TOKEN_VALUE)
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
|
||||
|
@ -97,6 +99,7 @@ public class OAuth2AccessTokenResponseTests {
|
|||
assertThat(tokenResponse.getAccessToken().getIssuedAt()).isNotNull();
|
||||
assertThat(tokenResponse.getAccessToken().getExpiresAt()).isAfterOrEqualTo(expiresAt);
|
||||
assertThat(tokenResponse.getAccessToken().getScopes()).isEqualTo(scopes);
|
||||
assertThat(tokenResponse.getRefreshToken().getTokenValue()).isEqualTo(REFRESH_TOKEN_VALUE);
|
||||
assertThat(tokenResponse.getAdditionalParameters()).isEqualTo(additionalParameters);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue