From 94324ebf0f68471bacc7d92865e07723b0dc3df7 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Wed, 16 Jun 2021 15:18:14 -0500 Subject: [PATCH] Support additional client authentication methods Closes gh-9780 --- .../registration/ClientRegistrations.java | 12 +-- .../ClientRegistrationsTests.java | 92 ++++++++++++++----- 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java index 9c8822d658f..65b3fbc849f 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistrations.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -239,8 +239,7 @@ private static ClientRegistration.Builder withProviderConfiguration(Authorizatio () -> "The Issuer \"" + metadataIssuer + "\" provided in the configuration metadata did " + "not match the requested issuer \"" + issuer + "\""); String name = URI.create(issuer).getHost(); - ClientAuthenticationMethod method = getClientAuthenticationMethod(issuer, - metadata.getTokenEndpointAuthMethods()); + ClientAuthenticationMethod method = getClientAuthenticationMethod(metadata.getTokenEndpointAuthMethods()); Map configurationMetadata = new LinkedHashMap<>(metadata.toJSONObject()); // @formatter:off return ClientRegistration.withRegistrationId(name) @@ -256,7 +255,7 @@ private static ClientRegistration.Builder withProviderConfiguration(Authorizatio // @formatter:on } - private static ClientAuthenticationMethod getClientAuthenticationMethod(String issuer, + private static ClientAuthenticationMethod getClientAuthenticationMethod( List metadataAuthMethods) { if (metadataAuthMethods == null || metadataAuthMethods .contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) { @@ -269,10 +268,7 @@ private static ClientAuthenticationMethod getClientAuthenticationMethod(String i if (metadataAuthMethods.contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.NONE)) { return ClientAuthenticationMethod.NONE; } - throw new IllegalArgumentException( - "Only ClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST and " - + "ClientAuthenticationMethod.NONE are supported. The issuer \"" + issuer - + "\" returned a configuration of " + metadataAuthMethods); + return null; } private interface ThrowingFunction { diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java index b31d308ffa8..117b7568cf6 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -274,6 +274,24 @@ public void issuerWhenOAuth2TokenEndpointAuthMethodsNullThenDefaulted() throws E .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); } + // gh-9780 + @Test + public void issuerWhenClientSecretBasicAuthMethodThenMethodIsBasic() throws Exception { + this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("client_secret_basic")); + ClientRegistration registration = registration("").build(); + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + } + + // gh-9780 + @Test + public void issuerWhenOAuth2ClientSecretBasicAuthMethodThenMethodIsBasic() throws Exception { + this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("client_secret_basic")); + ClientRegistration registration = registrationOAuth2("", null).build(); + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + } + @Test public void issuerWhenTokenEndpointAuthMethodsPostThenMethodIsPost() throws Exception { this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("client_secret_post")); @@ -290,6 +308,46 @@ public void issuerWhenOAuth2TokenEndpointAuthMethodsPostThenMethodIsPost() throw .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST); } + // gh-9780 + @Test + public void issuerWhenClientSecretJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception { + this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("client_secret_jwt")); + ClientRegistration registration = registration("").build(); + // The client_secret_basic auth method is still the default + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + } + + // gh-9780 + @Test + public void issuerWhenOAuth2ClientSecretJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception { + this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("client_secret_jwt")); + ClientRegistration registration = registrationOAuth2("", null).build(); + // The client_secret_basic auth method is still the default + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + } + + // gh-9780 + @Test + public void issuerWhenPrivateKeyJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception { + this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("private_key_jwt")); + ClientRegistration registration = registration("").build(); + // The client_secret_basic auth method is still the default + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + } + + // gh-9780 + @Test + public void issuerWhenOAuth2PrivateKeyJwtAuthMethodThenMethodIsClientSecretBasic() throws Exception { + this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("private_key_jwt")); + ClientRegistration registration = registrationOAuth2("", null).build(); + // The client_secret_basic auth method is still the default + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + } + @Test public void issuerWhenTokenEndpointAuthMethodsNoneThenMethodIsNone() throws Exception { this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("none")); @@ -304,32 +362,24 @@ public void issuerWhenOAuth2TokenEndpointAuthMethodsNoneThenMethodIsNone() throw assertThat(registration.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE); } - /** - * We currently only support client_secret_basic, so verify we have a meaningful error - * until we add support. - */ + // gh-9780 @Test - public void issuerWhenTokenEndpointAuthMethodsInvalidThenException() { + public void issuerWhenTlsClientAuthMethodThenSuccess() throws Exception { this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("tls_client_auth")); - // @formatter:off - assertThatIllegalArgumentException() - .isThrownBy(() -> registration("")) - .withMessageContaining("Only ClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST and " - + "ClientAuthenticationMethod.NONE are supported. The issuer \"" + this.issuer - + "\" returned a configuration of [tls_client_auth]"); - // @formatter:on + ClientRegistration registration = registration("").build(); + // The client_secret_basic auth method is still the default + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); } + // gh-9780 @Test - public void issuerWhenOAuth2TokenEndpointAuthMethodsInvalidThenException() { + public void issuerWhenOAuth2TlsClientAuthMethodThenSuccess() throws Exception { this.response.put("token_endpoint_auth_methods_supported", Arrays.asList("tls_client_auth")); - // @formatter:off - assertThatIllegalArgumentException() - .isThrownBy(() -> registrationOAuth2("", null)) - .withMessageContaining("Only ClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST and " - + "ClientAuthenticationMethod.NONE are supported. The issuer \"" + this.issuer - + "\" returned a configuration of [tls_client_auth]"); - // @formatter:on + ClientRegistration registration = registrationOAuth2("", null).build(); + // The client_secret_basic auth method is still the default + assertThat(registration.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); } @Test