1
0
mirror of synced 2026-08-05 09:47:05 +00:00

Use id_token for user authentication

Fixes gh-4410
This commit is contained in:
Joe Grandja
2017-06-27 11:45:19 -04:00
parent c986b6f4b5
commit 9cfb890207
43 changed files with 1328 additions and 583 deletions
@@ -15,25 +15,34 @@
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.jwt.JwtDecoder;
import org.springframework.security.jwt.nimbus.NimbusJwtDecoderJwkSupport;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger;
import org.springframework.security.oauth2.client.authentication.nimbus.NimbusAuthorizationCodeTokenExchanger;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.user.OAuth2UserService;
import org.springframework.security.oauth2.client.user.nimbus.NimbusOAuth2UserService;
import org.springframework.security.oauth2.client.authentication.jwt.DefaultProviderJwtDecoderRegistry;
import org.springframework.security.oauth2.core.provider.DefaultProviderMetadata;
import org.springframework.security.oauth2.client.authentication.jwt.ProviderJwtDecoderRegistry;
import org.springframework.security.oauth2.core.provider.ProviderMetadata;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* @author Joe Grandja
@@ -43,7 +52,8 @@ final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecuri
private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
private OAuth2UserService userInfoService;
private Map<URI, Function<ClientHttpResponse, ? extends OAuth2User>> userInfoTypeConverters = new HashMap<>();
private Map<URI, Class<? extends OAuth2User>> customUserTypes = new HashMap<>();
private Map<URI, String> userNameAttributeNames = new HashMap<>();
AuthorizationCodeAuthenticationFilterConfigurer() {
@@ -71,10 +81,17 @@ final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecuri
return this;
}
AuthorizationCodeAuthenticationFilterConfigurer<H> userInfoTypeConverter(Function<ClientHttpResponse, ? extends OAuth2User> userInfoConverter, URI userInfoUri) {
Assert.notNull(userInfoConverter, "userInfoConverter cannot be null");
AuthorizationCodeAuthenticationFilterConfigurer<H> customUserType(Class<? extends OAuth2User> customUserType, URI userInfoUri) {
Assert.notNull(customUserType, "customUserType cannot be null");
Assert.notNull(userInfoUri, "userInfoUri cannot be null");
this.userInfoTypeConverters.put(userInfoUri, userInfoConverter);
this.customUserTypes.put(userInfoUri, customUserType);
return this;
}
AuthorizationCodeAuthenticationFilterConfigurer<H> userNameAttributeName(String userNameAttributeName, URI userInfoUri) {
Assert.hasText(userNameAttributeName, "userNameAttributeName cannot be empty");
Assert.notNull(userInfoUri, "userInfoUri cannot be null");
this.userNameAttributeNames.put(userInfoUri, userNameAttributeName);
return this;
}
@@ -89,7 +106,7 @@ final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecuri
@Override
public void init(H http) throws Exception {
AuthorizationCodeAuthenticationProvider authenticationProvider = new AuthorizationCodeAuthenticationProvider(
this.getAuthorizationCodeTokenExchanger(), this.getUserInfoService());
this.getAuthorizationCodeTokenExchanger(), this.getProviderJwtDecoderRegistry(), this.getUserInfoService());
authenticationProvider = this.postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
super.init(http);
@@ -114,10 +131,51 @@ final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecuri
return this.authorizationCodeTokenExchanger;
}
private ProviderJwtDecoderRegistry getProviderJwtDecoderRegistry() {
Map<ProviderMetadata, JwtDecoder> jwtDecoders = new HashMap<>();
ClientRegistrationRepository clientRegistrationRepository = OAuth2LoginConfigurer.getClientRegistrationRepository(this.getBuilder());
clientRegistrationRepository.getRegistrations().stream().forEach(registration -> {
ClientRegistration.ProviderDetails providerDetails = registration.getProviderDetails();
if (StringUtils.hasText(providerDetails.getJwkSetUri())) {
DefaultProviderMetadata providerMetadata = new DefaultProviderMetadata();
// Default the Issuer to the host of the Authorization Endpoint
providerMetadata.setIssuer(this.toURL(
UriComponentsBuilder
.fromHttpUrl(providerDetails.getAuthorizationUri())
.replacePath(null)
.toUriString()
));
providerMetadata.setAuthorizationEndpoint(this.toURL(providerDetails.getAuthorizationUri()));
providerMetadata.setTokenEndpoint(this.toURL(providerDetails.getTokenUri()));
providerMetadata.setUserInfoEndpoint(this.toURL(providerDetails.getUserInfoUri()));
providerMetadata.setJwkSetUri(this.toURL(providerDetails.getJwkSetUri()));
jwtDecoders.put(providerMetadata, new NimbusJwtDecoderJwkSupport(providerDetails.getJwkSetUri()));
}
});
return new DefaultProviderJwtDecoderRegistry(jwtDecoders);
}
private OAuth2UserService getUserInfoService() {
if (this.userInfoService == null) {
this.userInfoService = new NimbusOAuth2UserService(this.userInfoTypeConverters);
this.userInfoService = new NimbusOAuth2UserService();
if (!this.customUserTypes.isEmpty()) {
((NimbusOAuth2UserService)this.userInfoService).setCustomUserTypes(this.customUserTypes);
}
if (!this.userNameAttributeNames.isEmpty()) {
((NimbusOAuth2UserService)this.userInfoService).setUserNameAttributeNames(this.userNameAttributeNames);
}
}
return this.userInfoService;
}
private URL toURL(String urlStr) {
if (!StringUtils.hasText(urlStr)) {
return null;
}
try {
return new URL(urlStr);
} catch (MalformedURLException ex) {
throw new IllegalArgumentException("Failed to convert '" + urlStr + "' to a URL: " + ex.getMessage(), ex);
}
}
}
@@ -16,7 +16,6 @@
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
import org.springframework.context.ApplicationContext;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationToken;
@@ -35,7 +34,6 @@ import org.springframework.util.CollectionUtils;
import java.net.URI;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -95,10 +93,17 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
return this.and();
}
public OAuth2LoginConfigurer<B> userInfoTypeConverter(Function<ClientHttpResponse, ? extends OAuth2User> userInfoConverter, URI userInfoUri) {
Assert.notNull(userInfoConverter, "userInfoConverter cannot be null");
public OAuth2LoginConfigurer<B> customUserType(Class<? extends OAuth2User> customUserType, URI userInfoUri) {
Assert.notNull(customUserType, "customUserType cannot be null");
Assert.notNull(userInfoUri, "userInfoUri cannot be null");
OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.userInfoTypeConverter(userInfoConverter, userInfoUri);
OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.customUserType(customUserType, userInfoUri);
return this.and();
}
public OAuth2LoginConfigurer<B> userNameAttributeName(String userNameAttributeName, URI userInfoUri) {
Assert.hasText(userNameAttributeName, "userNameAttributeName cannot be empty");
Assert.notNull(userInfoUri, "userInfoUri cannot be null");
OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.userNameAttributeName(userNameAttributeName, userInfoUri);
return this.and();
}