Merge branch 0.4.x into main
The following commits are merged using the default merge strategy.80b01854f2Update README with documentation links4d94e7095dDecompose OAuth2AuthorizationCodeRequestAuthenticationProvidercd6f1d7dc3Return registration_endpoint when client registration is enabled26aed3c183Polish gh-88192dbcf29a5Move integration tests for OidcProviderConfiguration
This commit is contained in:
@@ -18,7 +18,6 @@ package org.springframework.security.oauth2.server.authorization.authentication;
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
@@ -42,8 +41,6 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
@@ -58,7 +55,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -69,7 +65,8 @@ import static org.mockito.Mockito.when;
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);
|
||||
private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize";
|
||||
private static final String STATE = "state";
|
||||
private RegisteredClientRepository registeredClientRepository;
|
||||
private OAuth2AuthorizationService authorizationService;
|
||||
private OAuth2AuthorizationConsentService authorizationConsentService;
|
||||
@@ -132,19 +129,13 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
.hasMessage("authenticationValidator cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAuthorizationConsentCustomizerWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationConsentCustomizer(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("authorizationConsentCustomizer cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -160,9 +151,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri("https:///invalid")
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
"https:///invalid", STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -178,9 +169,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri("https://example.com#fragment")
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
"https://example.com#fragment", STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -196,9 +187,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri("https://localhost:5000")
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
"https://localhost:5000", STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -216,9 +207,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri("https://invalid-example.com")
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
"https://invalid-example.com", STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -236,9 +227,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri("https://127.0.0.1:5000")
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
"https://127.0.0.1:5000", STATE, registeredClient.getScopes(), null);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
@@ -255,9 +246,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri("https://[::1]:5000")
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
"https://[::1]:5000", STATE, registeredClient.getScopes(), null);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
@@ -271,9 +262,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri(null)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
null, STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -291,9 +282,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.redirectUri(null)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
null, STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -311,8 +302,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -328,9 +320,10 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(Collections.singleton("invalid-scope"))
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE,
|
||||
Collections.singleton("invalid-scope"), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -347,8 +340,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -366,9 +360,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, "code-challenge");
|
||||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "unsupported");
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), additionalParameters);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -386,9 +380,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
Map<String, Object> additionalParameters = new HashMap<>();
|
||||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, "code-challenge");
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), additionalParameters);
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
@@ -405,8 +399,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
this.principal.setAuthenticated(false);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
@@ -424,11 +419,12 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
.thenReturn(registeredClient);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
OAuth2AuthorizationConsentAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationConsentAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
|
||||
verify(this.authorizationService).save(authorizationCaptor.capture());
|
||||
@@ -457,8 +453,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
assertThat(authenticationResult.getAuthorizationUri()).isEqualTo(authorizationRequest.getAuthorizationUri());
|
||||
assertThat(authenticationResult.getScopes()).isEmpty();
|
||||
assertThat(authenticationResult.getState()).isEqualTo(state);
|
||||
assertThat(authenticationResult.isConsentRequired()).isTrue();
|
||||
assertThat(authenticationResult.getAuthorizationCode()).isNull();
|
||||
assertThat(authenticationResult.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@@ -475,8 +469,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
.thenReturn(registeredClient);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
@@ -500,8 +495,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
.thenReturn(previousAuthorizationConsent);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
@@ -519,9 +515,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, "code-challenge");
|
||||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256");
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), additionalParameters);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
@@ -540,8 +536,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
this.authenticationProvider.setAuthorizationCodeGenerator(authorizationCodeGenerator);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
@@ -563,8 +560,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
this.authenticationProvider.setAuthenticationValidator(authenticationValidator);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
@@ -611,410 +609,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
assertThat(authenticationResult.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestInvalidStateThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(null);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestPrincipalNotAuthenticatedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
this.principal.setAuthenticated(false);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestInvalidPrincipalThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName().concat("-other"))
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2()
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(otherRegisteredClient, this.principal)
|
||||
.state("state")
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestDoesNotMatchClientThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(otherRegisteredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.state("state")
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestScopeNotRequestedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = new HashSet<>(authorizationRequest.getScopes());
|
||||
authorizedScopes.add("scope-not-requested");
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(authorizedScopes)
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authorizationRequest.getRedirectUri())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestNotApprovedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(new HashSet<>()) // No scopes approved
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri())
|
||||
);
|
||||
|
||||
verify(this.authorizationService).remove(eq(authorization));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestApproveAllThenReturnAuthorizationCode() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = authorizationRequest.getScopes();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(authorizedScopes) // Approve all scopes
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomAuthorizationConsentCustomizerThenUsed() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = authorizationRequest.getScopes();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(authorizedScopes) // Approve all scopes
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer = mock(Consumer.class);
|
||||
this.authenticationProvider.setAuthorizationConsentCustomizer(authorizationConsentCustomizer);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult);
|
||||
|
||||
ArgumentCaptor<OAuth2AuthorizationConsentAuthenticationContext> authenticationContextCaptor =
|
||||
ArgumentCaptor.forClass(OAuth2AuthorizationConsentAuthenticationContext.class);
|
||||
verify(authorizationConsentCustomizer).accept(authenticationContextCaptor.capture());
|
||||
|
||||
OAuth2AuthorizationConsentAuthenticationContext authenticationContext = authenticationContextCaptor.getValue();
|
||||
assertThat(authenticationContext.<Authentication>getAuthentication()).isEqualTo(authentication);
|
||||
assertThat(authenticationContext.getAuthorizationConsent()).isNotNull();
|
||||
assertThat(authenticationContext.getRegisteredClient()).isEqualTo(registeredClient);
|
||||
assertThat(authenticationContext.getAuthorization()).isEqualTo(authorization);
|
||||
assertThat(authenticationContext.getAuthorizationRequest()).isEqualTo(authorizationRequest);
|
||||
}
|
||||
|
||||
private void assertAuthorizationConsentRequestWithAuthorizationCodeResult(
|
||||
RegisteredClient registeredClient,
|
||||
OAuth2Authorization authorization,
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) {
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = authorizationRequest.getScopes();
|
||||
|
||||
ArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationConsent.class);
|
||||
verify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());
|
||||
OAuth2AuthorizationConsent authorizationConsent = authorizationConsentCaptor.getValue();
|
||||
|
||||
assertThat(authorizationConsent.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());
|
||||
assertThat(authorizationConsent.getPrincipalName()).isEqualTo(authorization.getPrincipalName());
|
||||
assertThat(authorizationConsent.getAuthorities()).hasSize(authorizedScopes.size());
|
||||
assertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrderElementsOf(authorizedScopes);
|
||||
|
||||
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
|
||||
verify(this.authorizationService).save(authorizationCaptor.capture());
|
||||
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
|
||||
|
||||
assertThat(updatedAuthorization.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());
|
||||
assertThat(updatedAuthorization.getPrincipalName()).isEqualTo(authorization.getPrincipalName());
|
||||
assertThat(updatedAuthorization.getAuthorizationGrantType()).isEqualTo(authorization.getAuthorizationGrantType());
|
||||
assertThat(updatedAuthorization.<Authentication>getAttribute(Principal.class.getName()))
|
||||
.isEqualTo(authorization.<Authentication>getAttribute(Principal.class.getName()));
|
||||
assertThat(updatedAuthorization.<OAuth2AuthorizationRequest>getAttribute(OAuth2AuthorizationRequest.class.getName()))
|
||||
.isEqualTo(authorizationRequest);
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = updatedAuthorization.getToken(OAuth2AuthorizationCode.class);
|
||||
assertThat(authorizationCode).isNotNull();
|
||||
assertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE)).isNull();
|
||||
assertThat(updatedAuthorization.getAuthorizedScopes()).isEqualTo(authorizedScopes);
|
||||
|
||||
assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());
|
||||
assertThat(authenticationResult.getPrincipal()).isEqualTo(this.principal);
|
||||
assertThat(authenticationResult.getAuthorizationUri()).isEqualTo(authorizationRequest.getAuthorizationUri());
|
||||
assertThat(authenticationResult.getRedirectUri()).isEqualTo(authorizationRequest.getRedirectUri());
|
||||
assertThat(authenticationResult.getScopes()).isEqualTo(authorizedScopes);
|
||||
assertThat(authenticationResult.getState()).isEqualTo(authorizationRequest.getState());
|
||||
assertThat(authenticationResult.getAuthorizationCode()).isEqualTo(authorizationCode.getToken());
|
||||
assertThat(authenticationResult.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestApproveNoneAndRevokePreviouslyApprovedThenAuthorizationConsentRemoved() {
|
||||
String previouslyApprovedScope = "message.read";
|
||||
String requestedScope = "message.write";
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scopes(scopes -> {
|
||||
scopes.clear();
|
||||
scopes.add(previouslyApprovedScope);
|
||||
scopes.add(requestedScope);
|
||||
})
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(new HashSet<>()) // No scopes approved
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationConsent previousAuthorizationConsent =
|
||||
OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())
|
||||
.scope(previouslyApprovedScope)
|
||||
.build();
|
||||
when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName())))
|
||||
.thenReturn(previousAuthorizationConsent);
|
||||
|
||||
// Revoke all (including previously approved)
|
||||
this.authenticationProvider.setAuthorizationConsentCustomizer((authorizationConsentContext) ->
|
||||
authorizationConsentContext.getAuthorizationConsent().authorities(Set::clear));
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri())
|
||||
);
|
||||
|
||||
verify(this.authorizationConsentService).remove(eq(previousAuthorizationConsent));
|
||||
verify(this.authorizationService).remove(eq(authorization));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestApproveSomeAndPreviouslyApprovedThenAuthorizationConsentUpdated() {
|
||||
String previouslyApprovedScope = "message.read";
|
||||
String requestedScope = "message.write";
|
||||
String otherPreviouslyApprovedScope = "other.scope";
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scopes(scopes -> {
|
||||
scopes.clear();
|
||||
scopes.add(previouslyApprovedScope);
|
||||
scopes.add(requestedScope);
|
||||
})
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> requestedScopes = authorizationRequest.getScopes();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(requestedScopes)
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationConsent previousAuthorizationConsent =
|
||||
OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())
|
||||
.scope(previouslyApprovedScope)
|
||||
.scope(otherPreviouslyApprovedScope)
|
||||
.build();
|
||||
when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName())))
|
||||
.thenReturn(previousAuthorizationConsent);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
ArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationConsent.class);
|
||||
verify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());
|
||||
OAuth2AuthorizationConsent updatedAuthorizationConsent = authorizationConsentCaptor.getValue();
|
||||
|
||||
assertThat(updatedAuthorizationConsent.getRegisteredClientId()).isEqualTo(previousAuthorizationConsent.getRegisteredClientId());
|
||||
assertThat(updatedAuthorizationConsent.getPrincipalName()).isEqualTo(previousAuthorizationConsent.getPrincipalName());
|
||||
assertThat(updatedAuthorizationConsent.getScopes()).containsExactlyInAnyOrder(
|
||||
previouslyApprovedScope, otherPreviouslyApprovedScope, requestedScope);
|
||||
|
||||
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
|
||||
verify(this.authorizationService).save(authorizationCaptor.capture());
|
||||
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
|
||||
assertThat(updatedAuthorization.getAuthorizedScopes()).isEqualTo(requestedScopes);
|
||||
assertThat(authenticationResult.getScopes()).isEqualTo(requestedScopes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenConsentRequestApproveNoneAndPreviouslyApprovedThenAuthorizationConsentNotUpdated() {
|
||||
String previouslyApprovedScope = "message.read";
|
||||
String requestedScope = "message.write";
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scopes(scopes -> {
|
||||
scopes.clear();
|
||||
scopes.add(previouslyApprovedScope);
|
||||
scopes.add(requestedScope);
|
||||
})
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
authorizationConsentRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(new HashSet<>()) // No scopes approved
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationConsent previousAuthorizationConsent =
|
||||
OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())
|
||||
.scope(previouslyApprovedScope)
|
||||
.build();
|
||||
when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName())))
|
||||
.thenReturn(previousAuthorizationConsent);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
verify(this.authorizationConsentService, never()).save(any());
|
||||
assertThat(authenticationResult.getScopes()).isEqualTo(Collections.singleton(previouslyApprovedScope));
|
||||
}
|
||||
|
||||
private static void assertAuthenticationException(OAuth2AuthorizationCodeRequestAuthenticationException authenticationException,
|
||||
String errorCode, String parameterName, String redirectUri) {
|
||||
|
||||
@@ -1025,30 +619,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authenticationException.getAuthorizationCodeRequestAuthentication();
|
||||
assertThat(authorizationCodeRequestAuthentication.getRedirectUri()).isEqualTo(redirectUri);
|
||||
|
||||
// gh-595
|
||||
if (OAuth2ErrorCodes.ACCESS_DENIED.equals(errorCode)) {
|
||||
assertThat(authorizationCodeRequestAuthentication.isConsent()).isFalse();
|
||||
assertThat(authorizationCodeRequestAuthentication.isConsentRequired()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
private static OAuth2AuthorizationCodeRequestAuthenticationToken.Builder authorizationCodeRequestAuthentication(
|
||||
RegisteredClient registeredClient, Authentication principal) {
|
||||
return OAuth2AuthorizationCodeRequestAuthenticationToken.with(registeredClient.getClientId(), principal)
|
||||
.authorizationUri("https://provider.com/oauth2/authorize")
|
||||
.redirectUri(registeredClient.getRedirectUris().iterator().next())
|
||||
.scopes(registeredClient.getScopes())
|
||||
.state("state");
|
||||
}
|
||||
|
||||
private static OAuth2AuthorizationCodeRequestAuthenticationToken.Builder authorizationConsentRequestAuthentication(
|
||||
RegisteredClient registeredClient, Authentication principal) {
|
||||
return OAuth2AuthorizationCodeRequestAuthenticationToken.with(registeredClient.getClientId(), principal)
|
||||
.authorizationUri("https://provider.com/oauth2/authorize")
|
||||
.scopes(registeredClient.getScopes())
|
||||
.state("state")
|
||||
.consent(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,61 +38,57 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
*/
|
||||
public class OAuth2AuthorizationCodeRequestAuthenticationTokenTests {
|
||||
private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize";
|
||||
private static final String STATE = "state";
|
||||
private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();
|
||||
private static final TestingAuthenticationToken PRINCIPAL = new TestingAuthenticationToken("principalName", "password");
|
||||
private static final OAuth2AuthorizationCode AUTHORIZATION_CODE =
|
||||
new OAuth2AuthorizationCode("code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES));
|
||||
|
||||
@Test
|
||||
public void withWhenClientIdNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> OAuth2AuthorizationCodeRequestAuthenticationToken.with(null, PRINCIPAL))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("clientId cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withWhenPrincipalNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> OAuth2AuthorizationCodeRequestAuthenticationToken.with(REGISTERED_CLIENT.getClientId(), null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("principal cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenAuthorizationUriNotProvidedThenThrowIllegalArgumentException() {
|
||||
public void constructorWhenAuthorizationUriNotProvidedThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() ->
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(REGISTERED_CLIENT.getClientId(), PRINCIPAL)
|
||||
.build())
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(null, REGISTERED_CLIENT.getClientId(), PRINCIPAL,
|
||||
null, null, (Set<String>) null, null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("authorizationUri cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenStateNotProvidedThenThrowIllegalArgumentException() {
|
||||
public void constructorWhenClientIdNotProvidedThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() ->
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(REGISTERED_CLIENT.getClientId(), PRINCIPAL)
|
||||
.authorizationUri(AUTHORIZATION_URI)
|
||||
.consent(true)
|
||||
.build())
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, null, PRINCIPAL,
|
||||
null, null, (Set<String>) null, null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("state cannot be empty");
|
||||
.hasMessage("clientId cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenAuthorizationCodeRequestThenValuesAreSet() {
|
||||
public void constructorWhenPrincipalNotProvidedThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() ->
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, REGISTERED_CLIENT.getClientId(), null,
|
||||
null, null, (Set<String>) null, null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("principal cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenAuthorizationCodeNotProvidedThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() ->
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, REGISTERED_CLIENT.getClientId(), PRINCIPAL,
|
||||
null, null, null, (Set<String>) null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("authorizationCode cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenAuthorizationRequestThenValuesAreSet() {
|
||||
String clientId = REGISTERED_CLIENT.getClientId();
|
||||
String redirectUri = REGISTERED_CLIENT.getRedirectUris().iterator().next();
|
||||
String state = "state";
|
||||
Set<String> requestedScopes = REGISTERED_CLIENT.getScopes();
|
||||
Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1");
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(clientId, PRINCIPAL)
|
||||
.authorizationUri(AUTHORIZATION_URI)
|
||||
.redirectUri(redirectUri)
|
||||
.scopes(requestedScopes)
|
||||
.state(STATE)
|
||||
.additionalParameters(additionalParameters)
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, clientId, PRINCIPAL, redirectUri, state, requestedScopes, additionalParameters);
|
||||
|
||||
assertThat(authentication.getPrincipal()).isEqualTo(PRINCIPAL);
|
||||
assertThat(authentication.getCredentials()).isEqualTo("");
|
||||
@@ -100,87 +96,22 @@ public class OAuth2AuthorizationCodeRequestAuthenticationTokenTests {
|
||||
assertThat(authentication.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);
|
||||
assertThat(authentication.getClientId()).isEqualTo(clientId);
|
||||
assertThat(authentication.getRedirectUri()).isEqualTo(redirectUri);
|
||||
assertThat(authentication.getState()).isEqualTo(state);
|
||||
assertThat(authentication.getScopes()).containsExactlyInAnyOrderElementsOf(requestedScopes);
|
||||
assertThat(authentication.getState()).isEqualTo(STATE);
|
||||
assertThat(authentication.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf(additionalParameters);
|
||||
assertThat(authentication.isConsentRequired()).isFalse();
|
||||
assertThat(authentication.isConsent()).isFalse();
|
||||
assertThat(authentication.getAuthorizationCode()).isNull();
|
||||
assertThat(authentication.isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenAuthorizationConsentRequiredThenValuesAreSet() {
|
||||
String clientId = REGISTERED_CLIENT.getClientId();
|
||||
Set<String> authorizedScopes = REGISTERED_CLIENT.getScopes();
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(clientId, PRINCIPAL)
|
||||
.authorizationUri(AUTHORIZATION_URI)
|
||||
.scopes(authorizedScopes)
|
||||
.state(STATE)
|
||||
.consentRequired(true)
|
||||
.build();
|
||||
|
||||
assertThat(authentication.getPrincipal()).isEqualTo(PRINCIPAL);
|
||||
assertThat(authentication.getCredentials()).isEqualTo("");
|
||||
assertThat(authentication.getAuthorities()).isEmpty();
|
||||
assertThat(authentication.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);
|
||||
assertThat(authentication.getClientId()).isEqualTo(clientId);
|
||||
assertThat(authentication.getRedirectUri()).isNull();
|
||||
assertThat(authentication.getScopes()).containsExactlyInAnyOrderElementsOf(authorizedScopes);
|
||||
assertThat(authentication.getState()).isEqualTo(STATE);
|
||||
assertThat(authentication.getAdditionalParameters()).isEmpty();
|
||||
assertThat(authentication.isConsentRequired()).isTrue();
|
||||
assertThat(authentication.isConsent()).isFalse();
|
||||
assertThat(authentication.getAuthorizationCode()).isNull();
|
||||
assertThat(authentication.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenAuthorizationConsentRequestThenValuesAreSet() {
|
||||
String clientId = REGISTERED_CLIENT.getClientId();
|
||||
Set<String> authorizedScopes = REGISTERED_CLIENT.getScopes();
|
||||
Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1");
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(clientId, PRINCIPAL)
|
||||
.authorizationUri(AUTHORIZATION_URI)
|
||||
.scopes(authorizedScopes)
|
||||
.state(STATE)
|
||||
.additionalParameters(additionalParameters)
|
||||
.consent(true)
|
||||
.build();
|
||||
|
||||
assertThat(authentication.getPrincipal()).isEqualTo(PRINCIPAL);
|
||||
assertThat(authentication.getCredentials()).isEqualTo("");
|
||||
assertThat(authentication.getAuthorities()).isEmpty();
|
||||
assertThat(authentication.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);
|
||||
assertThat(authentication.getClientId()).isEqualTo(clientId);
|
||||
assertThat(authentication.getRedirectUri()).isNull();
|
||||
assertThat(authentication.getScopes()).containsExactlyInAnyOrderElementsOf(authorizedScopes);
|
||||
assertThat(authentication.getState()).isEqualTo(STATE);
|
||||
assertThat(authentication.getAdditionalParameters()).containsExactlyInAnyOrderEntriesOf(additionalParameters);
|
||||
assertThat(authentication.isConsentRequired()).isFalse();
|
||||
assertThat(authentication.isConsent()).isTrue();
|
||||
assertThat(authentication.getAuthorizationCode()).isNull();
|
||||
assertThat(authentication.isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenAuthorizationResponseThenValuesAreSet() {
|
||||
public void constructorWhenAuthorizationResponseThenValuesAreSet() {
|
||||
String clientId = REGISTERED_CLIENT.getClientId();
|
||||
String redirectUri = REGISTERED_CLIENT.getRedirectUris().iterator().next();
|
||||
String state = "state";
|
||||
Set<String> authorizedScopes = REGISTERED_CLIENT.getScopes();
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(clientId, PRINCIPAL)
|
||||
.authorizationUri(AUTHORIZATION_URI)
|
||||
.redirectUri(redirectUri)
|
||||
.scopes(authorizedScopes)
|
||||
.state(STATE)
|
||||
.authorizationCode(AUTHORIZATION_CODE)
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, clientId, PRINCIPAL, AUTHORIZATION_CODE, redirectUri, state, authorizedScopes);
|
||||
|
||||
assertThat(authentication.getPrincipal()).isEqualTo(PRINCIPAL);
|
||||
assertThat(authentication.getCredentials()).isEqualTo("");
|
||||
@@ -188,11 +119,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationTokenTests {
|
||||
assertThat(authentication.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI);
|
||||
assertThat(authentication.getClientId()).isEqualTo(clientId);
|
||||
assertThat(authentication.getRedirectUri()).isEqualTo(redirectUri);
|
||||
assertThat(authentication.getState()).isEqualTo(state);
|
||||
assertThat(authentication.getScopes()).containsExactlyInAnyOrderElementsOf(authorizedScopes);
|
||||
assertThat(authentication.getState()).isEqualTo(STATE);
|
||||
assertThat(authentication.getAdditionalParameters()).isEmpty();
|
||||
assertThat(authentication.isConsentRequired()).isFalse();
|
||||
assertThat(authentication.isConsent()).isFalse();
|
||||
assertThat(authentication.getAuthorizationCode()).isEqualTo(AUTHORIZATION_CODE);
|
||||
assertThat(authentication.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
* Copyright 2020-2022 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.
|
||||
@@ -42,10 +42,10 @@ public class OAuth2AuthorizationConsentAuthenticationContextTests {
|
||||
private final Authentication principal = this.authorization.getAttribute(Principal.class.getName());
|
||||
private final OAuth2AuthorizationRequest authorizationRequest = this.authorization.getAttribute(
|
||||
OAuth2AuthorizationRequest.class.getName());
|
||||
private final OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(this.registeredClient.getClientId(), this.principal)
|
||||
.authorizationUri(this.authorizationRequest.getAuthorizationUri())
|
||||
.build();
|
||||
private final OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
this.authorizationRequest.getAuthorizationUri(), this.registeredClient.getClientId(),
|
||||
this.principal, "state", null, null);
|
||||
private final OAuth2AuthorizationConsent.Builder authorizationConsentBuilder =
|
||||
OAuth2AuthorizationConsent.withId(this.authorization.getRegisteredClientId(), this.authorization.getPrincipalName());
|
||||
|
||||
@@ -59,7 +59,7 @@ public class OAuth2AuthorizationConsentAuthenticationContextTests {
|
||||
@Test
|
||||
public void setWhenValueNullThenThrowIllegalArgumentException() {
|
||||
OAuth2AuthorizationConsentAuthenticationContext.Builder builder =
|
||||
OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationCodeRequestAuthentication);
|
||||
OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationConsentAuthentication);
|
||||
|
||||
assertThatThrownBy(() -> builder.authorizationConsent(null))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
@@ -76,7 +76,7 @@ public class OAuth2AuthorizationConsentAuthenticationContextTests {
|
||||
@Test
|
||||
public void buildWhenRequiredValueNullThenThrowIllegalArgumentException() {
|
||||
OAuth2AuthorizationConsentAuthenticationContext.Builder builder =
|
||||
OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationCodeRequestAuthentication);
|
||||
OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationConsentAuthentication);
|
||||
|
||||
assertThatThrownBy(builder::build)
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
@@ -104,7 +104,7 @@ public class OAuth2AuthorizationConsentAuthenticationContextTests {
|
||||
@Test
|
||||
public void buildWhenAllValuesProvidedThenAllValuesAreSet() {
|
||||
OAuth2AuthorizationConsentAuthenticationContext context =
|
||||
OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationCodeRequestAuthentication)
|
||||
OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationConsentAuthentication)
|
||||
.authorizationConsent(this.authorizationConsentBuilder)
|
||||
.registeredClient(this.registeredClient)
|
||||
.authorization(this.authorization)
|
||||
@@ -113,7 +113,7 @@ public class OAuth2AuthorizationConsentAuthenticationContextTests {
|
||||
.context(ctx -> ctx.put("custom-key-2", "custom-value-2"))
|
||||
.build();
|
||||
|
||||
assertThat(context.<Authentication>getAuthentication()).isEqualTo(this.authorizationCodeRequestAuthentication);
|
||||
assertThat(context.<Authentication>getAuthentication()).isEqualTo(this.authorizationConsentAuthentication);
|
||||
assertThat(context.getAuthorizationConsent()).isEqualTo(this.authorizationConsentBuilder);
|
||||
assertThat(context.getRegisteredClient()).isEqualTo(this.registeredClient);
|
||||
assertThat(context.getAuthorization()).isEqualTo(this.authorization);
|
||||
|
||||
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* Copyright 2020-2022 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
|
||||
*
|
||||
* https://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.server.authorization.authentication;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
|
||||
import org.springframework.security.oauth2.server.authorization.context.TestAuthorizationServerContext;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link OAuth2AuthorizationConsentAuthenticationProvider}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class OAuth2AuthorizationConsentAuthenticationProviderTests {
|
||||
private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize";
|
||||
private static final String STATE = "state";
|
||||
private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);
|
||||
private RegisteredClientRepository registeredClientRepository;
|
||||
private OAuth2AuthorizationService authorizationService;
|
||||
private OAuth2AuthorizationConsentService authorizationConsentService;
|
||||
private OAuth2AuthorizationConsentAuthenticationProvider authenticationProvider;
|
||||
private TestingAuthenticationToken principal;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.registeredClientRepository = mock(RegisteredClientRepository.class);
|
||||
this.authorizationService = mock(OAuth2AuthorizationService.class);
|
||||
this.authorizationConsentService = mock(OAuth2AuthorizationConsentService.class);
|
||||
this.authenticationProvider = new OAuth2AuthorizationConsentAuthenticationProvider(
|
||||
this.registeredClientRepository, this.authorizationService, this.authorizationConsentService);
|
||||
this.principal = new TestingAuthenticationToken("principalName", "password");
|
||||
this.principal.setAuthenticated(true);
|
||||
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build();
|
||||
AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(
|
||||
null, this.authorizationService, this.authorizationConsentService))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("registeredClientRepository cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(
|
||||
this.registeredClientRepository, null, this.authorizationConsentService))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("authorizationService cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenAuthorizationConsentServiceNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(
|
||||
this.registeredClientRepository, this.authorizationService, null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("authorizationConsentService cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsWhenTypeOAuth2AuthorizationConsentAuthenticationTokenThenReturnTrue() {
|
||||
assertThat(this.authenticationProvider.supports(OAuth2AuthorizationConsentAuthenticationToken.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAuthorizationCodeGeneratorWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationCodeGenerator(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("authorizationCodeGenerator cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAuthorizationConsentCustomizerWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationConsentCustomizer(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("authorizationConsentCustomizer cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenInvalidStateThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, registeredClient.getScopes(), null);
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(null);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, registeredClient.getScopes(), null);
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
this.principal.setAuthenticated(false);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenInvalidPrincipalThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName().concat("-other"))
|
||||
.build();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, registeredClient.getScopes(), null);
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2()
|
||||
.build();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, otherRegisteredClient.getClientId(), principal,
|
||||
STATE, registeredClient.getScopes(), null);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenDoesNotMatchClientThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2()
|
||||
.build();
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(otherRegisteredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, registeredClient.getScopes(), null);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenScopeNotRequestedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = new HashSet<>(authorizationRequest.getScopes());
|
||||
authorizedScopes.add("scope-not-requested");
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, authorizedScopes, null);
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authorizationRequest.getRedirectUri())
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenNotApprovedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, new HashSet<>(), null); // No scopes approved
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri())
|
||||
);
|
||||
|
||||
verify(this.authorizationService).remove(eq(authorization));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenApproveAllThenReturnAuthorizationCode() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = authorizationRequest.getScopes();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, authorizedScopes, null); // Approve all scopes
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomAuthorizationConsentCustomizerThenUsed() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = authorizationRequest.getScopes();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, authorizedScopes, null); // Approve all scopes
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer = mock(Consumer.class);
|
||||
this.authenticationProvider.setAuthorizationConsentCustomizer(authorizationConsentCustomizer);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult);
|
||||
|
||||
ArgumentCaptor<OAuth2AuthorizationConsentAuthenticationContext> authenticationContextCaptor =
|
||||
ArgumentCaptor.forClass(OAuth2AuthorizationConsentAuthenticationContext.class);
|
||||
verify(authorizationConsentCustomizer).accept(authenticationContextCaptor.capture());
|
||||
|
||||
OAuth2AuthorizationConsentAuthenticationContext authenticationContext = authenticationContextCaptor.getValue();
|
||||
assertThat(authenticationContext.<Authentication>getAuthentication()).isEqualTo(authentication);
|
||||
assertThat(authenticationContext.getAuthorizationConsent()).isNotNull();
|
||||
assertThat(authenticationContext.getRegisteredClient()).isEqualTo(registeredClient);
|
||||
assertThat(authenticationContext.getAuthorization()).isEqualTo(authorization);
|
||||
assertThat(authenticationContext.getAuthorizationRequest()).isEqualTo(authorizationRequest);
|
||||
}
|
||||
|
||||
private void assertAuthorizationConsentRequestWithAuthorizationCodeResult(
|
||||
RegisteredClient registeredClient,
|
||||
OAuth2Authorization authorization,
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) {
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> authorizedScopes = authorizationRequest.getScopes();
|
||||
|
||||
ArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationConsent.class);
|
||||
verify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());
|
||||
OAuth2AuthorizationConsent authorizationConsent = authorizationConsentCaptor.getValue();
|
||||
|
||||
assertThat(authorizationConsent.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());
|
||||
assertThat(authorizationConsent.getPrincipalName()).isEqualTo(authorization.getPrincipalName());
|
||||
assertThat(authorizationConsent.getAuthorities()).hasSize(authorizedScopes.size());
|
||||
assertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrderElementsOf(authorizedScopes);
|
||||
|
||||
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
|
||||
verify(this.authorizationService).save(authorizationCaptor.capture());
|
||||
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
|
||||
|
||||
assertThat(updatedAuthorization.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());
|
||||
assertThat(updatedAuthorization.getPrincipalName()).isEqualTo(authorization.getPrincipalName());
|
||||
assertThat(updatedAuthorization.getAuthorizationGrantType()).isEqualTo(authorization.getAuthorizationGrantType());
|
||||
assertThat(updatedAuthorization.<Authentication>getAttribute(Principal.class.getName()))
|
||||
.isEqualTo(authorization.<Authentication>getAttribute(Principal.class.getName()));
|
||||
assertThat(updatedAuthorization.<OAuth2AuthorizationRequest>getAttribute(OAuth2AuthorizationRequest.class.getName()))
|
||||
.isEqualTo(authorizationRequest);
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = updatedAuthorization.getToken(OAuth2AuthorizationCode.class);
|
||||
assertThat(authorizationCode).isNotNull();
|
||||
assertThat(updatedAuthorization.<String>getAttribute(OAuth2ParameterNames.STATE)).isNull();
|
||||
assertThat(updatedAuthorization.getAuthorizedScopes()).isEqualTo(authorizedScopes);
|
||||
|
||||
assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId());
|
||||
assertThat(authenticationResult.getPrincipal()).isEqualTo(this.principal);
|
||||
assertThat(authenticationResult.getAuthorizationUri()).isEqualTo(authorizationRequest.getAuthorizationUri());
|
||||
assertThat(authenticationResult.getRedirectUri()).isEqualTo(authorizationRequest.getRedirectUri());
|
||||
assertThat(authenticationResult.getScopes()).isEqualTo(authorizedScopes);
|
||||
assertThat(authenticationResult.getState()).isEqualTo(authorizationRequest.getState());
|
||||
assertThat(authenticationResult.getAuthorizationCode()).isEqualTo(authorizationCode.getToken());
|
||||
assertThat(authenticationResult.isAuthenticated()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenApproveNoneAndRevokePreviouslyApprovedThenAuthorizationConsentRemoved() {
|
||||
String previouslyApprovedScope = "message.read";
|
||||
String requestedScope = "message.write";
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scopes(scopes -> {
|
||||
scopes.clear();
|
||||
scopes.add(previouslyApprovedScope);
|
||||
scopes.add(requestedScope);
|
||||
})
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, new HashSet<>(), null); // No scopes approved
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationConsent previousAuthorizationConsent =
|
||||
OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())
|
||||
.scope(previouslyApprovedScope)
|
||||
.build();
|
||||
when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName())))
|
||||
.thenReturn(previousAuthorizationConsent);
|
||||
|
||||
// Revoke all (including previously approved)
|
||||
this.authenticationProvider.setAuthorizationConsentCustomizer((authorizationConsentContext) ->
|
||||
authorizationConsentContext.getAuthorizationConsent().authorities(Set::clear));
|
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
|
||||
.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
|
||||
.satisfies(ex ->
|
||||
assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
|
||||
OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri())
|
||||
);
|
||||
|
||||
verify(this.authorizationConsentService).remove(eq(previousAuthorizationConsent));
|
||||
verify(this.authorizationService).remove(eq(authorization));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenApproveSomeAndPreviouslyApprovedThenAuthorizationConsentUpdated() {
|
||||
String previouslyApprovedScope = "message.read";
|
||||
String requestedScope = "message.write";
|
||||
String otherPreviouslyApprovedScope = "other.scope";
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scopes(scopes -> {
|
||||
scopes.clear();
|
||||
scopes.add(previouslyApprovedScope);
|
||||
scopes.add(requestedScope);
|
||||
})
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> requestedScopes = authorizationRequest.getScopes();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, requestedScopes, null);
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationConsent previousAuthorizationConsent =
|
||||
OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())
|
||||
.scope(previouslyApprovedScope)
|
||||
.scope(otherPreviouslyApprovedScope)
|
||||
.build();
|
||||
when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName())))
|
||||
.thenReturn(previousAuthorizationConsent);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
ArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationConsent.class);
|
||||
verify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());
|
||||
OAuth2AuthorizationConsent updatedAuthorizationConsent = authorizationConsentCaptor.getValue();
|
||||
|
||||
assertThat(updatedAuthorizationConsent.getRegisteredClientId()).isEqualTo(previousAuthorizationConsent.getRegisteredClientId());
|
||||
assertThat(updatedAuthorizationConsent.getPrincipalName()).isEqualTo(previousAuthorizationConsent.getPrincipalName());
|
||||
assertThat(updatedAuthorizationConsent.getScopes()).containsExactlyInAnyOrder(
|
||||
previouslyApprovedScope, otherPreviouslyApprovedScope, requestedScope);
|
||||
|
||||
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
|
||||
verify(this.authorizationService).save(authorizationCaptor.capture());
|
||||
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
|
||||
assertThat(updatedAuthorization.getAuthorizedScopes()).isEqualTo(requestedScopes);
|
||||
assertThat(authenticationResult.getScopes()).isEqualTo(requestedScopes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenApproveNoneAndPreviouslyApprovedThenAuthorizationConsentNotUpdated() {
|
||||
String previouslyApprovedScope = "message.read";
|
||||
String requestedScope = "message.write";
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
|
||||
.scopes(scopes -> {
|
||||
scopes.clear();
|
||||
scopes.add(previouslyApprovedScope);
|
||||
scopes.add(requestedScope);
|
||||
})
|
||||
.build();
|
||||
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
|
||||
.thenReturn(registeredClient);
|
||||
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
|
||||
.principalName(this.principal.getName())
|
||||
.build();
|
||||
OAuth2AuthorizationConsentAuthenticationToken authentication =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, new HashSet<>(), null); // No scopes approved
|
||||
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
|
||||
.thenReturn(authorization);
|
||||
OAuth2AuthorizationConsent previousAuthorizationConsent =
|
||||
OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName())
|
||||
.scope(previouslyApprovedScope)
|
||||
.build();
|
||||
when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName())))
|
||||
.thenReturn(previousAuthorizationConsent);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
|
||||
|
||||
verify(this.authorizationConsentService, never()).save(any());
|
||||
assertThat(authenticationResult.getScopes()).isEqualTo(Collections.singleton(previouslyApprovedScope));
|
||||
}
|
||||
|
||||
private static void assertAuthenticationException(OAuth2AuthorizationCodeRequestAuthenticationException authenticationException,
|
||||
String errorCode, String parameterName, String redirectUri) {
|
||||
|
||||
OAuth2Error error = authenticationException.getError();
|
||||
assertThat(error.getErrorCode()).isEqualTo(errorCode);
|
||||
assertThat(error.getDescription()).contains(parameterName);
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authenticationException.getAuthorizationCodeRequestAuthentication();
|
||||
assertThat(authorizationCodeRequestAuthentication.getRedirectUri()).isEqualTo(redirectUri);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -90,6 +90,8 @@ import org.springframework.security.oauth2.server.authorization.TestOAuth2Author
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationContext;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
@@ -108,6 +110,7 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
@@ -626,13 +629,9 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(
|
||||
"code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES));
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken.with(registeredClient.getClientId(), principal)
|
||||
.authorizationUri("https://provider.com/oauth2/authorize")
|
||||
.redirectUri(registeredClient.getRedirectUris().iterator().next())
|
||||
.scopes(registeredClient.getScopes())
|
||||
.state("state")
|
||||
.authorizationCode(authorizationCode)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
"https://provider.com/oauth2/authorize", registeredClient.getClientId(), principal, authorizationCode,
|
||||
registeredClient.getRedirectUris().iterator().next(), "state", registeredClient.getScopes());
|
||||
when(authorizationRequestConverter.convert(any())).thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
when(authorizationRequestAuthenticationProvider.supports(eq(OAuth2AuthorizationCodeRequestAuthenticationToken.class))).thenReturn(true);
|
||||
when(authorizationRequestAuthenticationProvider.authenticate(any())).thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
@@ -650,7 +649,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
List<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();
|
||||
assertThat(authenticationConverters).allMatch((converter) ->
|
||||
converter == authorizationRequestConverter ||
|
||||
converter instanceof OAuth2AuthorizationCodeRequestAuthenticationConverter);
|
||||
converter instanceof OAuth2AuthorizationCodeRequestAuthenticationConverter ||
|
||||
converter instanceof OAuth2AuthorizationConsentAuthenticationConverter);
|
||||
|
||||
verify(authorizationRequestAuthenticationProvider).authenticate(eq(authorizationCodeRequestAuthenticationResult));
|
||||
|
||||
@@ -660,7 +660,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
List<AuthenticationProvider> authenticationProviders = authenticationProvidersCaptor.getValue();
|
||||
assertThat(authenticationProviders).allMatch((provider) ->
|
||||
provider == authorizationRequestAuthenticationProvider ||
|
||||
provider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider);
|
||||
provider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider ||
|
||||
provider instanceof OAuth2AuthorizationConsentAuthenticationProvider);
|
||||
|
||||
verify(authorizationResponseHandler).onAuthenticationSuccess(any(), any(), eq(authorizationCodeRequestAuthenticationResult));
|
||||
}
|
||||
@@ -933,7 +934,7 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
new OAuth2AuthorizationServerConfigurer();
|
||||
authorizationServerConfigurer
|
||||
.authorizationEndpoint(authorizationEndpoint ->
|
||||
authorizationEndpoint.authenticationProvider(createProvider()));
|
||||
authorizationEndpoint.authenticationProviders(configureAuthenticationProviders()));
|
||||
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
|
||||
|
||||
http
|
||||
@@ -965,15 +966,14 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
};
|
||||
}
|
||||
|
||||
private AuthenticationProvider createProvider() {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider =
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationProvider(
|
||||
this.registeredClientRepository,
|
||||
this.authorizationService,
|
||||
this.authorizationConsentService);
|
||||
authorizationCodeRequestAuthenticationProvider.setAuthorizationConsentCustomizer(new AuthorizationConsentCustomizer());
|
||||
|
||||
return authorizationCodeRequestAuthenticationProvider;
|
||||
private Consumer<List<AuthenticationProvider>> configureAuthenticationProviders() {
|
||||
return (authenticationProviders) ->
|
||||
authenticationProviders.forEach((authenticationProvider) -> {
|
||||
if (authenticationProvider instanceof OAuth2AuthorizationConsentAuthenticationProvider) {
|
||||
((OAuth2AuthorizationConsentAuthenticationProvider) authenticationProvider)
|
||||
.setAuthorizationConsentCustomizer(new AuthorizationConsentCustomizer());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class AuthorizationConsentCustomizer implements Consumer<OAuth2AuthorizationConsentAuthenticationContext> {
|
||||
@@ -982,10 +982,10 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
public void accept(OAuth2AuthorizationConsentAuthenticationContext authorizationConsentAuthenticationContext) {
|
||||
OAuth2AuthorizationConsent.Builder authorizationConsentBuilder =
|
||||
authorizationConsentAuthenticationContext.getAuthorizationConsent();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication =
|
||||
authorizationConsentAuthenticationContext.getAuthentication();
|
||||
Map<String, Object> additionalParameters =
|
||||
authorizationCodeRequestAuthentication.getAdditionalParameters();
|
||||
authorizationConsentAuthentication.getAdditionalParameters();
|
||||
RegisteredClient registeredClient = authorizationConsentAuthenticationContext.getRegisteredClient();
|
||||
ClientSettings clientSettings = registeredClient.getClientSettings();
|
||||
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright 2020-2022 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
|
||||
*
|
||||
* https://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.server.authorization.config.annotation.web.configurers;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames;
|
||||
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.test.SpringTestRule;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hamcrest.CoreMatchers.hasItems;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Integration tests for the OpenID Connect 1.0 Provider Configuration endpoint.
|
||||
*
|
||||
* @author Sahariar Alam Khandoker
|
||||
* @author Joe Grandja
|
||||
* @author Daniel Garnier-Moiroux
|
||||
*/
|
||||
public class OidcProviderConfigurationTests {
|
||||
private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration";
|
||||
private static final String ISSUER_URL = "https://example.com/issuer1";
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
|
||||
@Autowired
|
||||
private AuthorizationServerSettings authorizationServerSettings;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mvc;
|
||||
|
||||
@Test
|
||||
public void requestWhenConfigurationRequestAndIssuerSetThenReturnDefaultConfigurationResponse() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfiguration.class).autowire();
|
||||
|
||||
this.mvc.perform(get(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpectAll(defaultConfigurationMatchers());
|
||||
}
|
||||
|
||||
// gh-632
|
||||
@Test
|
||||
public void requestWhenConfigurationRequestAndUserAuthenticatedThenReturnConfigurationResponse() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfiguration.class).autowire();
|
||||
|
||||
this.mvc.perform(get(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)
|
||||
.with(user("user")))
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpectAll(defaultConfigurationMatchers());
|
||||
}
|
||||
|
||||
// gh-616
|
||||
@Test
|
||||
public void requestWhenConfigurationRequestAndConfigurationCustomizerSetThenReturnCustomConfigurationResponse() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfigurationWithProviderConfigurationCustomizer.class).autowire();
|
||||
|
||||
this.mvc.perform(get(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
|
||||
hasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenConfigurationRequestAndClientRegistrationEnabledThenConfigurationResponseIncludesRegistrationEndpoint() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfigurationWithClientRegistrationEnabled.class).autowire();
|
||||
|
||||
this.mvc.perform(get(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpectAll(defaultConfigurationMatchers())
|
||||
.andExpect(jsonPath("$.registration_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint())));
|
||||
}
|
||||
|
||||
private ResultMatcher[] defaultConfigurationMatchers() {
|
||||
// @formatter:off
|
||||
return new ResultMatcher[] {
|
||||
jsonPath("issuer").value(ISSUER_URL),
|
||||
jsonPath("authorization_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getAuthorizationEndpoint())),
|
||||
jsonPath("token_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getTokenEndpoint())),
|
||||
jsonPath("$.token_endpoint_auth_methods_supported[0]").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
|
||||
jsonPath("$.token_endpoint_auth_methods_supported[1]").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
|
||||
jsonPath("$.token_endpoint_auth_methods_supported[2]").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),
|
||||
jsonPath("$.token_endpoint_auth_methods_supported[3]").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),
|
||||
jsonPath("jwks_uri").value(ISSUER_URL.concat(this.authorizationServerSettings.getJwkSetEndpoint())),
|
||||
jsonPath("userinfo_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getOidcUserInfoEndpoint())),
|
||||
jsonPath("response_types_supported").value(OAuth2AuthorizationResponseType.CODE.getValue()),
|
||||
jsonPath("$.grant_types_supported[0]").value(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()),
|
||||
jsonPath("$.grant_types_supported[1]").value(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()),
|
||||
jsonPath("$.grant_types_supported[2]").value(AuthorizationGrantType.REFRESH_TOKEN.getValue()),
|
||||
jsonPath("revocation_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getTokenRevocationEndpoint())),
|
||||
jsonPath("$.revocation_endpoint_auth_methods_supported[0]").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
|
||||
jsonPath("$.revocation_endpoint_auth_methods_supported[1]").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
|
||||
jsonPath("$.revocation_endpoint_auth_methods_supported[2]").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),
|
||||
jsonPath("$.revocation_endpoint_auth_methods_supported[3]").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),
|
||||
jsonPath("introspection_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getTokenIntrospectionEndpoint())),
|
||||
jsonPath("$.introspection_endpoint_auth_methods_supported[0]").value(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()),
|
||||
jsonPath("$.introspection_endpoint_auth_methods_supported[1]").value(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()),
|
||||
jsonPath("$.introspection_endpoint_auth_methods_supported[2]").value(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()),
|
||||
jsonPath("$.introspection_endpoint_auth_methods_supported[3]").value(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()),
|
||||
jsonPath("subject_types_supported").value("public"),
|
||||
jsonPath("id_token_signing_alg_values_supported").value(SignatureAlgorithm.RS256.getName()),
|
||||
jsonPath("scopes_supported").value(OidcScopes.OPENID)
|
||||
};
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerNotValidUrlThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUrl.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerNotValidUriThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUri.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithQueryThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerQuery.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithFragmentThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerFragment.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithQueryAndFragmentThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerQueryAndFragment.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithEmptyQueryThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyQuery.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithEmptyFragmentThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyFragment.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
RegisteredClientRepository registeredClientRepository() {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
return new InMemoryRegisteredClientRepository(registeredClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder()
|
||||
.issuer(ISSUER_URL)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class AuthorizationServerConfigurationWithProviderConfigurationCustomizer extends AuthorizationServerConfiguration {
|
||||
|
||||
// @formatter:off
|
||||
@Bean
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
|
||||
new OAuth2AuthorizationServerConfigurer();
|
||||
http.apply(authorizationServerConfigurer);
|
||||
|
||||
authorizationServerConfigurer
|
||||
.oidc(oidc ->
|
||||
oidc.providerConfigurationEndpoint(providerConfigurationEndpoint ->
|
||||
providerConfigurationEndpoint
|
||||
.providerConfigurationCustomizer(providerConfigurationCustomizer())));
|
||||
|
||||
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
|
||||
|
||||
http
|
||||
.requestMatcher(endpointsMatcher)
|
||||
.authorizeRequests(authorizeRequests ->
|
||||
authorizeRequests.anyRequest().authenticated()
|
||||
)
|
||||
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher));
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
private Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer() {
|
||||
return (providerConfiguration) ->
|
||||
providerConfiguration.scope(OidcScopes.PROFILE).scope(OidcScopes.EMAIL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class AuthorizationServerConfigurationWithClientRegistrationEnabled extends AuthorizationServerConfiguration {
|
||||
|
||||
// @formatter:off
|
||||
@Bean
|
||||
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
|
||||
new OAuth2AuthorizationServerConfigurer();
|
||||
http.apply(authorizationServerConfigurer);
|
||||
|
||||
authorizationServerConfigurer
|
||||
.oidc(oidc ->
|
||||
oidc.clientRegistrationEndpoint(Customizer.withDefaults())
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithInvalidIssuerUrl extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer("urn:example").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithInvalidIssuerUri extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer("https://not a valid uri").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerQuery extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerFragment extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#fragment").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerQueryAndFragment extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value#fragment").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerEmptyQuery extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerEmptyFragment extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#").build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import java.util.Base64;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
@@ -37,7 +36,6 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -71,7 +69,6 @@ import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
|
||||
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
@@ -82,8 +79,6 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.oauth2.server.authorization.jackson2.TestingAuthenticationTokenMixin;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.OidcProviderConfiguration;
|
||||
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.test.SpringTestRule;
|
||||
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
|
||||
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
|
||||
@@ -103,9 +98,7 @@ import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.hasItems;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -126,8 +119,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
public class OidcTests {
|
||||
private static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = "/oauth2/authorize";
|
||||
private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token";
|
||||
private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration";
|
||||
private static final String ISSUER_URL = "https://example.com/issuer1";
|
||||
private static final String AUTHORITIES_CLAIM = "authorities";
|
||||
private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);
|
||||
private static EmbeddedDatabase db;
|
||||
@@ -182,85 +173,6 @@ public class OidcTests {
|
||||
db.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenConfigurationRequestAndIssuerSetThenReturnConfigurationResponse() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfigurationWithIssuer.class).autowire();
|
||||
|
||||
this.mvc.perform(get(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpect(jsonPath("issuer").value(ISSUER_URL));
|
||||
}
|
||||
|
||||
// gh-632
|
||||
@Test
|
||||
public void requestWhenConfigurationRequestAndUserAuthenticatedThenReturnConfigurationResponse() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfiguration.class).autowire();
|
||||
|
||||
this.mvc.perform(get(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)
|
||||
.with(user("user")))
|
||||
.andExpect(status().is2xxSuccessful());
|
||||
}
|
||||
|
||||
// gh-616
|
||||
@Test
|
||||
public void requestWhenConfigurationRequestAndConfigurationCustomizerSetThenReturnCustomConfigurationResponse() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfigurationWithProviderConfigurationCustomizer.class).autowire();
|
||||
|
||||
this.mvc.perform(get(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))
|
||||
.andExpect(status().is2xxSuccessful())
|
||||
.andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
|
||||
hasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerNotValidUrlThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUrl.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerNotValidUriThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUri.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithQueryThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerQuery.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithFragmentThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerFragment.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithQueryAndFragmentThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerQueryAndFragment.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithEmptyQueryThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyQuery.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadContextWhenIssuerWithEmptyFragmentThenThrowException() {
|
||||
assertThatThrownBy(
|
||||
() -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyFragment.class).autowire()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthenticationRequestThenTokenResponseIncludesIdToken() throws Exception {
|
||||
this.spring.register(AuthorizationServerConfiguration.class).autowire();
|
||||
@@ -481,121 +393,4 @@ public class OidcTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class AuthorizationServerConfigurationWithProviderConfigurationCustomizer extends AuthorizationServerConfiguration {
|
||||
|
||||
// @formatter:off
|
||||
@Bean
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
|
||||
new OAuth2AuthorizationServerConfigurer();
|
||||
http.apply(authorizationServerConfigurer);
|
||||
|
||||
authorizationServerConfigurer
|
||||
.oidc(oidc ->
|
||||
oidc.providerConfigurationEndpoint(providerConfigurationEndpoint ->
|
||||
providerConfigurationEndpoint
|
||||
.providerConfigurationCustomizer(providerConfigurationCustomizer())));
|
||||
|
||||
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
|
||||
|
||||
http
|
||||
.requestMatcher(endpointsMatcher)
|
||||
.authorizeRequests(authorizeRequests ->
|
||||
authorizeRequests.anyRequest().authenticated()
|
||||
)
|
||||
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher));
|
||||
|
||||
return http.build();
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
private Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer() {
|
||||
return (providerConfiguration) ->
|
||||
providerConfiguration.scope(OidcScopes.PROFILE).scope(OidcScopes.EMAIL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuer extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL).build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithInvalidIssuerUrl extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer("urn:example").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithInvalidIssuerUri extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer("https://not a valid uri").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerQuery extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerFragment extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#fragment").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerQueryAndFragment extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value#fragment").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerEmptyQuery extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?").build();
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(OAuth2AuthorizationServerConfiguration.class)
|
||||
static class AuthorizationServerConfigurationWithIssuerEmptyFragment extends AuthorizationServerConfiguration {
|
||||
|
||||
@Bean
|
||||
AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#").build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ public class OidcProviderConfigurationTests {
|
||||
.idTokenSigningAlgorithm("RS256")
|
||||
.userInfoEndpoint("https://example.com/issuer1/userinfo")
|
||||
.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
|
||||
.clientRegistrationEndpoint("https://example.com/issuer1/connect/register")
|
||||
.claim("a-claim", "a-value")
|
||||
.build();
|
||||
|
||||
@@ -75,6 +76,7 @@ public class OidcProviderConfigurationTests {
|
||||
assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
|
||||
assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
|
||||
assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
|
||||
assertThat(providerConfiguration.getClientRegistrationEndpoint()).isEqualTo(url("https://example.com/issuer1/connect/register"));
|
||||
assertThat(providerConfiguration.<String>getClaim("a-claim")).isEqualTo("a-value");
|
||||
}
|
||||
|
||||
@@ -115,6 +117,7 @@ public class OidcProviderConfigurationTests {
|
||||
claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public"));
|
||||
claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256"));
|
||||
claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "https://example.com/issuer1/userinfo");
|
||||
claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, "https://example.com/issuer1/connect/register");
|
||||
claims.put("some-claim", "some-value");
|
||||
|
||||
OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();
|
||||
@@ -130,6 +133,7 @@ public class OidcProviderConfigurationTests {
|
||||
assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
|
||||
assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
|
||||
assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();
|
||||
assertThat(providerConfiguration.getClientRegistrationEndpoint()).isEqualTo(url("https://example.com/issuer1/connect/register"));
|
||||
assertThat(providerConfiguration.<String>getClaim("some-claim")).isEqualTo("some-value");
|
||||
}
|
||||
|
||||
@@ -145,6 +149,7 @@ public class OidcProviderConfigurationTests {
|
||||
claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public"));
|
||||
claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256"));
|
||||
claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, url("https://example.com/issuer1/userinfo"));
|
||||
claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, url("https://example.com/issuer1/connect/register"));
|
||||
claims.put("some-claim", "some-value");
|
||||
|
||||
OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();
|
||||
@@ -160,6 +165,7 @@ public class OidcProviderConfigurationTests {
|
||||
assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
|
||||
assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
|
||||
assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();
|
||||
assertThat(providerConfiguration.getClientRegistrationEndpoint()).isEqualTo(url("https://example.com/issuer1/connect/register"));
|
||||
assertThat(providerConfiguration.<String>getClaim("some-claim")).isEqualTo("some-value");
|
||||
}
|
||||
|
||||
@@ -396,6 +402,16 @@ public class OidcProviderConfigurationTests {
|
||||
.withMessage("userInfoEndpoint must be a valid URL");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildWhenClientRegistrationEndpointNotUrlThenThrowIllegalArgumentException() {
|
||||
OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder
|
||||
.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, "not an url"));
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(builder::build)
|
||||
.withMessage("clientRegistrationEndpoint must be a valid URL");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseTypesWhenAddingOrRemovingThenCorrectValues() {
|
||||
OidcProviderConfiguration configuration = this.minimalConfigurationBuilder
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
@@ -52,6 +51,7 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
@@ -82,6 +82,8 @@ import static org.mockito.Mockito.when;
|
||||
*/
|
||||
public class OAuth2AuthorizationEndpointFilterTests {
|
||||
private static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = "/oauth2/authorize";
|
||||
private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize";
|
||||
private static final String STATE = "state";
|
||||
private static final String REMOTE_ADDRESS = "remote-address";
|
||||
private AuthenticationManager authenticationManager;
|
||||
private OAuth2AuthorizationEndpointFilter filter;
|
||||
@@ -280,8 +282,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
public void doFilterWhenAuthorizationRequestAuthenticationExceptionThenErrorResponse() throws Exception {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
OAuth2Error error = new OAuth2Error("errorCode", "errorDescription", "errorUri");
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenThrow(new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthentication));
|
||||
@@ -304,8 +307,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
|
||||
AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);
|
||||
when(authenticationConverter.convert(any())).thenReturn(authorizationCodeRequestAuthentication);
|
||||
@@ -329,9 +333,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.authorizationCode(this.authorizationCode)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(true);
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
@@ -354,8 +358,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
OAuth2Error error = new OAuth2Error("errorCode", "errorDescription", "errorUri");
|
||||
OAuth2AuthorizationCodeRequestAuthenticationException authenticationException =
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthentication);
|
||||
@@ -380,7 +385,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
public void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal).build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
MockHttpServletRequest request = createAuthorizationRequest(registeredClient);
|
||||
|
||||
AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource =
|
||||
@@ -407,8 +414,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
this.principal.setAuthenticated(false);
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null);
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(false);
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
@@ -432,14 +440,13 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
scopes.addAll(requestedScopes);
|
||||
})
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(new HashSet<>()) // No scopes previously approved
|
||||
.consentRequired(true)
|
||||
.build();
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(true);
|
||||
OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, new HashSet<>(), null); // No scopes previously approved
|
||||
authorizationConsentAuthenticationResult.setAuthenticated(true);
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
.thenReturn(authorizationConsentAuthenticationResult);
|
||||
|
||||
MockHttpServletRequest request = createAuthorizationRequest(registeredClient);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -464,14 +471,13 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
scopes.addAll(requestedScopes);
|
||||
})
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(new HashSet<>()) // No scopes previously approved
|
||||
.consentRequired(true)
|
||||
.build();
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(true);
|
||||
OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, new HashSet<>(), null); // No scopes previously approved
|
||||
authorizationConsentAuthenticationResult.setAuthenticated(true);
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
.thenReturn(authorizationConsentAuthenticationResult);
|
||||
|
||||
MockHttpServletRequest request = createAuthorizationRequest(registeredClient);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -500,14 +506,13 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
scopes.addAll(requestedScopes);
|
||||
})
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.scopes(approvedScopes)
|
||||
.consentRequired(true)
|
||||
.build();
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(true);
|
||||
OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult =
|
||||
new OAuth2AuthorizationConsentAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal,
|
||||
STATE, approvedScopes, null);
|
||||
authorizationConsentAuthenticationResult.setAuthenticated(true);
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
.thenReturn(authorizationConsentAuthenticationResult);
|
||||
|
||||
MockHttpServletRequest request = createAuthorizationRequest(registeredClient);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -532,9 +537,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
public void doFilterWhenAuthorizationRequestAuthenticatedThenAuthorizationResponse() throws Exception {
|
||||
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.authorizationCode(this.authorizationCode)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(true);
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
@@ -568,9 +573,9 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
})
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
authorizationCodeRequestAuthentication(registeredClient, this.principal)
|
||||
.authorizationCode(this.authorizationCode)
|
||||
.build();
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode,
|
||||
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(true);
|
||||
when(this.authenticationManager.authenticate(any()))
|
||||
.thenReturn(authorizationCodeRequestAuthenticationResult);
|
||||
@@ -647,15 +652,6 @@ public class OAuth2AuthorizationEndpointFilterTests {
|
||||
return request;
|
||||
}
|
||||
|
||||
private static OAuth2AuthorizationCodeRequestAuthenticationToken.Builder authorizationCodeRequestAuthentication(
|
||||
RegisteredClient registeredClient, Authentication principal) {
|
||||
return OAuth2AuthorizationCodeRequestAuthenticationToken.with(registeredClient.getClientId(), principal)
|
||||
.authorizationUri("https://provider.com/oauth2/authorize")
|
||||
.redirectUri(registeredClient.getRedirectUris().iterator().next())
|
||||
.scopes(registeredClient.getScopes())
|
||||
.state("state");
|
||||
}
|
||||
|
||||
private static String scopeCheckbox(String scope) {
|
||||
return MessageFormat.format(
|
||||
"<input class=\"form-check-input\" type=\"checkbox\" name=\"scope\" value=\"{0}\" id=\"{0}\">",
|
||||
|
||||
Reference in New Issue
Block a user