Skip to content

Updated OAuth 2.0 Issuer-Based Auto Configuration #17761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private static Builder getBuilderFromIssuerIfPossible(String registrationId, Str
Provider provider = providers.get(providerId);
String issuer = provider.getIssuerUri();
if (issuer != null) {
Builder builder = ClientRegistrations.fromOidcIssuerLocation(issuer).registrationId(registrationId);
Builder builder = ClientRegistrations.fromIssuerLocation(issuer).registrationId(registrationId);
return getBuilder(builder, provider);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private byte[] getKeySpec(String keyValue) {
@Bean
@Conditional(IssuerUriCondition.class)
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
return ReactiveJwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private byte[] getKeySpec(String keyValue) {
@Bean
@Conditional(IssuerUriCondition.class)
JwtDecoder jwtDecoderByIssuerUri() {
return JwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
return JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,22 @@ void oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Ex
testOidcConfiguration(login, "okta");
}

@Test
void oidcRfc8414ProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Exception {
OAuth2ClientProperties.Registration login = new Registration();
login.setClientId("clientId");
login.setClientSecret("clientSecret");
testOidcRfc8414Configuration(login, "okta");
}

@Test
void oAuthProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Exception {
OAuth2ClientProperties.Registration login = new Registration();
login.setClientId("clientId");
login.setClientSecret("clientSecret");
testOAuthConfiguration(login, "okta");
}

@Test
void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception {
OAuth2ClientProperties.Registration login = new Registration();
Expand All @@ -221,6 +237,24 @@ void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Excep
testOidcConfiguration(login, "okta-oidc");
}

@Test
void oidcRfc8414ProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception {
OAuth2ClientProperties.Registration login = new Registration();
login.setProvider("okta-oidcRfc8414");
login.setClientId("clientId");
login.setClientSecret("clientSecret");
testOidcRfc8414Configuration(login, "okta-oidcRfc8414");
}

@Test
void oAuthProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception {
OAuth2ClientProperties.Registration login = new Registration();
login.setProvider("okta-oauth");
login.setClientId("clientId");
login.setClientSecret("clientSecret");
testOAuthConfiguration(login, "okta-oauth");
}

@Test
void oidcProviderConfigurationWithCustomConfigurationOverridesProviderDefaults() throws Exception {
this.server = new MockWebServer();
Expand Down Expand Up @@ -300,6 +334,70 @@ private void testOidcConfiguration(OAuth2ClientProperties.Registration registrat
assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo");
assertThat(userInfoEndpoint.getAuthenticationMethod())
.isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER);
assertThat(this.server.getRequestCount()).isEqualTo(1);
}

private void testOidcRfc8414Configuration(OAuth2ClientProperties.Registration registration, String providerId)
throws Exception {
this.server = new MockWebServer();
this.server.start();
String path = "test";
String issuer = this.server.url(path).toString();
setupMockResponseWithEmptyResponses(issuer, 1);
OAuth2ClientProperties properties = new OAuth2ClientProperties();
Provider provider = new Provider();
provider.setIssuerUri(issuer);
properties.getProvider().put(providerId, provider);
properties.getRegistration().put("okta", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("okta");
ProviderDetails providerDetails = adapted.getProviderDetails();
assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC);
assertThat(adapted.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(adapted.getRegistrationId()).isEqualTo("okta");
assertThat(adapted.getClientName()).isEqualTo(issuer);
assertThat(adapted.getScopes()).containsOnly("openid");
assertThat(providerDetails.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth");
assertThat(providerDetails.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token");
assertThat(providerDetails.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs");
UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();
assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo");
assertThat(userInfoEndpoint.getAuthenticationMethod())
.isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER);
assertThat(this.server.getRequestCount()).isEqualTo(2);

}

private void testOAuthConfiguration(OAuth2ClientProperties.Registration registration, String providerId)
throws Exception {
this.server = new MockWebServer();
this.server.start();
String path = "test";
String issuer = this.server.url(path).toString();
setupMockResponseWithEmptyResponses(issuer, 2);
OAuth2ClientProperties properties = new OAuth2ClientProperties();
Provider provider = new Provider();
provider.setIssuerUri(issuer);
properties.getProvider().put(providerId, provider);
properties.getRegistration().put("okta", registration);
Map<String, ClientRegistration> registrations = OAuth2ClientPropertiesRegistrationAdapter
.getClientRegistrations(properties);
ClientRegistration adapted = registrations.get("okta");
ProviderDetails providerDetails = adapted.getProviderDetails();
assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC);
assertThat(adapted.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE);
assertThat(adapted.getRegistrationId()).isEqualTo("okta");
assertThat(adapted.getClientName()).isEqualTo(issuer);
assertThat(adapted.getScopes()).containsOnly("openid");
assertThat(providerDetails.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth");
assertThat(providerDetails.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token");
assertThat(providerDetails.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs");
UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint();
assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo");
assertThat(userInfoEndpoint.getAuthenticationMethod())
.isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER);
assertThat(this.server.getRequestCount()).isEqualTo(3);
}

private void setupMockResponse(String issuer) throws JsonProcessingException {
Expand All @@ -309,6 +407,15 @@ private void setupMockResponse(String issuer) throws JsonProcessingException {
this.server.enqueue(mockResponse);
}

private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse)
throws JsonProcessingException {
for (int i = 0; i < amountOfEmptyResponse; i++) {
MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value());
this.server.enqueue(emptyResponse);
}
setupMockResponse(issuer);
}

private Map<String, Object> getResponse(String issuer) {
Map<String, Object> response = new HashMap<>();
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,51 @@ void autoConfigurationShouldConfigureResourceServer() {
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws IOException {
this.server = new MockWebServer();
this.server.start();
String issuer = this.server.url("").toString();
String path = "test";
String issuer = this.server.url(path).toString();
String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponse(cleanIssuerPath);
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
assertFilterConfiguredWithJwtAuthenticationManager(context);
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
});
assertThat(this.server.getRequestCount()).isEqualTo(1);
}

@Test
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception {
this.server = new MockWebServer();
this.server.start();
String path = "test";
String issuer = this.server.url(path).toString();
String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponseWithEmptyResponses(cleanIssuerPath, 1);
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
assertFilterConfiguredWithJwtAuthenticationManager(context);
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
});
assertThat(this.server.getRequestCount()).isEqualTo(2);
}

@Test
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception {
this.server = new MockWebServer();
this.server.start();
String path = "test";
String issuer = this.server.url(path).toString();
String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponseWithEmptyResponses(cleanIssuerPath, 2);
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
assertFilterConfiguredWithJwtAuthenticationManager(context);
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
});
assertThat(this.server.getRequestCount()).isEqualTo(3);
}

@Test
Expand Down Expand Up @@ -322,6 +359,15 @@ private void setupMockResponse(String issuer) throws JsonProcessingException {
this.server.enqueue(mockResponse);
}

private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse)
throws JsonProcessingException {
for (int i = 0; i < amountOfEmptyResponse; i++) {
MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value());
this.server.enqueue(emptyResponse);
}
setupMockResponse(issuer);
}

private Map<String, Object> getResponse(String issuer) {
Map<String, Object> response = new HashMap<>();
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,48 @@ void autoConfigurationShouldConfigureResourceServerWithJwsAlgorithm() {
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws Exception {
this.server = new MockWebServer();
this.server.start();
String issuer = this.server.url("").toString();
String path = "test";
String issuer = this.server.url(path).toString();
String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponse(cleanIssuerPath);
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
assertThat(context).hasSingleBean(JwtDecoder.class);
assertThat(getBearerTokenFilter(context)).isNotNull();
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
});
assertThat(this.server.getRequestCount()).isEqualTo(1);
}

@Test
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception {
this.server = new MockWebServer();
this.server.start();
String path = "test";
String issuer = this.server.url(path).toString();
String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponseWithEmptyResponses(cleanIssuerPath, 1);
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
assertThat(context).hasSingleBean(JwtDecoder.class);
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
});
assertThat(this.server.getRequestCount()).isEqualTo(2);
}

@Test
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception {
this.server = new MockWebServer();
this.server.start();
String path = "test";
String issuer = this.server.url(path).toString();
String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponseWithEmptyResponses(cleanIssuerPath, 2);
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
assertThat(context).hasSingleBean(JwtDecoder.class);
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
});
assertThat(this.server.getRequestCount()).isEqualTo(3);
}

@Test
Expand Down Expand Up @@ -306,6 +340,15 @@ private void setupMockResponse(String issuer) throws JsonProcessingException {
this.server.enqueue(mockResponse);
}

private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse)
throws JsonProcessingException {
for (int i = 0; i < amountOfEmptyResponse; i++) {
MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value());
this.server.enqueue(emptyResponse);
}
setupMockResponse(issuer);
}

private Map<String, Object> getResponse(String issuer) {
Map<String, Object> response = new HashMap<>();
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");
Expand Down