Require code_verifier if code_challenge provided

Closes gh-453
This commit is contained in:
Daniel Garnier-Moiroux
2021-10-22 11:56:41 +02:00
committed by Joe Grandja
parent defd1f90b8
commit 088d9a8e34
6 changed files with 81 additions and 25 deletions

View File

@@ -370,6 +370,35 @@ public class OAuth2AuthorizationCodeGrantTests {
assertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME)).isEqualTo(true);
}
@Test
public void requestWhenConfidentialClientWithPkceAndMissingCodeVerifierThenUnauthorized() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
this.registeredClientRepository.save(registeredClient);
MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
.params(getAuthorizationRequestParameters(registeredClient))
.param(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE)
.param(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256")
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
assertThat(redirectedUrl).matches("https://example.com\\?code=.{15,}&state=state");
String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code");
OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE);
assertThat(authorizationCodeAuthorization).isNotNull();
assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
.params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization))
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
.header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient)))
.andExpect(status().isUnauthorized());
}
@Test
public void requestWhenCustomJwtEncoderThenUsed() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithJwtEncoder.class).autowire();

View File

@@ -36,7 +36,6 @@ import org.springframework.security.oauth2.server.authorization.TestOAuth2Author
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.ClientSettings;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -182,6 +181,26 @@ public class OAuth2ClientAuthenticationProviderTests {
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
}
@Test
public void authenticateWhenAuthorizationCodeGrantAndValidCredentialsThenAuthenticated() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))
.thenReturn(TestOAuth2Authorizations.authorization().build());
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), createAuthorizationCodeTokenParameters());
OAuth2ClientAuthenticationToken authenticationResult =
(OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication);
verify(this.passwordEncoder).matches(any(), any());
assertThat(authenticationResult.isAuthenticated()).isTrue();
assertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId());
assertThat(authenticationResult.getCredentials().toString()).isEqualTo(registeredClient.getClientSecret());
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
}
@Test
public void authenticateWhenPkceAndInvalidCodeThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();
@@ -208,20 +227,18 @@ public class OAuth2ClientAuthenticationProviderTests {
}
@Test
public void authenticateWhenPkceAndRequireProofKeyAndMissingCodeChallengeThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient()
.clientSettings(ClientSettings.builder().requireProofKey(true).build())
.build();
public void authenticateWhenPkceAndPublicClientAndMissingCodeVerifierThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations
.authorization(registeredClient)
.authorization(registeredClient, createPkceAuthorizationParametersPlain())
.build();
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))
.thenReturn(authorization);
Map<String, Object> parameters = createPkceTokenParameters(PLAIN_CODE_VERIFIER);
Map<String, Object> parameters = createAuthorizationCodeTokenParameters();
OAuth2ClientAuthenticationToken authentication =
new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);
@@ -234,8 +251,8 @@ public class OAuth2ClientAuthenticationProviderTests {
}
@Test
public void authenticateWhenPkceAndMissingCodeVerifierThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build();
public void authenticateWhenPkceAndConfidentialClientAndMissingCodeVerifierThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
@@ -245,11 +262,10 @@ public class OAuth2ClientAuthenticationProviderTests {
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)))
.thenReturn(authorization);
Map<String, Object> parameters = createPkceTokenParameters(PLAIN_CODE_VERIFIER);
parameters.remove(PkceParameterNames.CODE_VERIFIER);
Map<String, Object> parameters = createAuthorizationCodeTokenParameters();
OAuth2ClientAuthenticationToken authentication =
new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters);
new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), parameters);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
@@ -425,10 +441,15 @@ public class OAuth2ClientAuthenticationProviderTests {
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
private static Map<String, Object> createPkceTokenParameters(String codeVerifier) {
private static Map<String, Object> createAuthorizationCodeTokenParameters() {
Map<String, Object> parameters = new HashMap<>();
parameters.put(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());
parameters.put(OAuth2ParameterNames.CODE, AUTHORIZATION_CODE);
return parameters;
}
private static Map<String, Object> createPkceTokenParameters(String codeVerifier) {
Map<String, Object> parameters = createAuthorizationCodeTokenParameters();
parameters.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);
return parameters;
}