Use id_token for user authentication
Fixes gh-4410
This commit is contained in:
+66
-8
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user