X509 client certificate authentication triggers when client id is provided

Closes gh-1635
This commit is contained in:
Joe Grandja
2024-06-12 05:40:51 -04:00
parent 25b70bcafe
commit e3c6effeea
5 changed files with 35 additions and 11 deletions

View File

@@ -240,10 +240,10 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
authenticationConverters.add(new JwtClientAssertionAuthenticationConverter());
authenticationConverters.add(new X509ClientCertificateAuthenticationConverter());
authenticationConverters.add(new ClientSecretBasicAuthenticationConverter());
authenticationConverters.add(new ClientSecretPostAuthenticationConverter());
authenticationConverters.add(new PublicClientAuthenticationConverter());
authenticationConverters.add(new X509ClientCertificateAuthenticationConverter());
return authenticationConverters;
}

View File

@@ -114,10 +114,10 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter
this.authenticationConverter = new DelegatingAuthenticationConverter(
Arrays.asList(
new JwtClientAssertionAuthenticationConverter(),
new X509ClientCertificateAuthenticationConverter(),
new ClientSecretBasicAuthenticationConverter(),
new ClientSecretPostAuthenticationConverter(),
new PublicClientAuthenticationConverter()));
new PublicClientAuthenticationConverter(),
new X509ClientCertificateAuthenticationConverter()));
// @formatter:on
}

View File

@@ -59,7 +59,11 @@ public final class X509ClientCertificateAuthenticationConverter implements Authe
// client_id (REQUIRED)
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
if (!StringUtils.hasText(clientId)) {
return null;
}
if (parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
}

View File

@@ -309,6 +309,28 @@ public class OAuth2ClientCredentialsGrantTests {
verify(jwtCustomizer).customize(any());
}
// gh-1635
@Test
public void requestWhenTokenRequestIncludesBasicClientCredentialsAndX509ClientCertificateThenTokenResponse()
throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
this.registeredClientRepository.save(registeredClient);
this.mvc
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI).with(x509(TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2")
.header(HttpHeaders.AUTHORIZATION,
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
verify(jwtCustomizer).customize(any());
}
@Test
public void requestWhenTokenEndpointCustomizedThenUsed() throws Exception {
this.spring.register(AuthorizationServerConfigurationCustomTokenEndpoint.class).autowire();
@@ -394,10 +416,10 @@ public class OAuth2ClientCredentialsGrantTests {
List<AuthenticationConverter> authenticationConverters = authenticationConvertersCaptor.getValue();
assertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter
|| converter instanceof JwtClientAssertionAuthenticationConverter
|| converter instanceof X509ClientCertificateAuthenticationConverter
|| converter instanceof ClientSecretBasicAuthenticationConverter
|| converter instanceof ClientSecretPostAuthenticationConverter
|| converter instanceof PublicClientAuthenticationConverter);
|| converter instanceof PublicClientAuthenticationConverter
|| converter instanceof X509ClientCertificateAuthenticationConverter);
verify(authenticationProvider).authenticate(eq(clientPrincipal));

View File

@@ -58,14 +58,12 @@ public class X509ClientCertificateAuthenticationConverterTests {
}
@Test
public void convertWhenMissingClientIdThenInvalidRequestError() {
public void convertWhenMissingClientIdThenReturnNull() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute("jakarta.servlet.request.X509Certificate",
TestX509Certificates.DEMO_CLIENT_PKI_CERTIFICATE);
assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class)
.extracting((ex) -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNull();
}
@Test