Introduce OidcUserInfoAuthenticationContext
Issue gh-441
This commit is contained in:
@@ -22,12 +22,10 @@ import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2Token;
|
||||
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter;
|
||||
@@ -46,7 +44,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
*/
|
||||
public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
private RequestMatcher requestMatcher;
|
||||
private Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper;
|
||||
private Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper;
|
||||
|
||||
/**
|
||||
* Restrict for internal use only.
|
||||
@@ -56,22 +54,22 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext}
|
||||
* Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext}
|
||||
* to an instance of {@link OidcUserInfo} for the UserInfo response.
|
||||
*
|
||||
* <p>
|
||||
* The {@link OAuth2AuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken}.
|
||||
* In addition, the following context attributes are supported:
|
||||
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken},
|
||||
* as well as, the following context attributes:
|
||||
* <ul>
|
||||
* <li>{@code OAuth2Token.class} - The {@link OAuth2Token} containing the bearer token used to make the request.</li>
|
||||
* <li>{@code OAuth2Authorization.class} - The {@link OAuth2Authorization} containing the {@link OidcIdToken} and
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and
|
||||
* {@link OAuth2AccessToken} associated with the bearer token used to make the request.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcUserInfoEndpointConfigurer userInfoMapper(Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper) {
|
||||
public OidcUserInfoEndpointConfigurer userInfoMapper(Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {
|
||||
this.userInfoMapper = userInfoMapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2020-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.server.authorization.oidc.authentication;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2AuthenticationContext} that holds an {@link OidcUserInfoAuthenticationToken} and additional information
|
||||
* and is used when mapping claims to an instance of {@link OidcUserInfo}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.2.1
|
||||
* @see OAuth2AuthenticationContext
|
||||
* @see OidcUserInfoAuthenticationProvider#setUserInfoMapper(Function)
|
||||
*/
|
||||
public final class OidcUserInfoAuthenticationContext extends OAuth2AuthenticationContext {
|
||||
|
||||
private OidcUserInfoAuthenticationContext(Map<Object, Object> context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2AccessToken OAuth 2.0 Access Token}.
|
||||
*
|
||||
* @return the {@link OAuth2AccessToken}
|
||||
*/
|
||||
public OAuth2AccessToken getAccessToken() {
|
||||
return get(OAuth2AccessToken.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2Authorization authorization}.
|
||||
*
|
||||
* @return the {@link OAuth2Authorization}
|
||||
*/
|
||||
public OAuth2Authorization getAuthorization() {
|
||||
return get(OAuth2Authorization.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with the provided {@link OidcUserInfoAuthenticationToken}.
|
||||
*
|
||||
* @param authentication the {@link OidcUserInfoAuthenticationToken}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder with(OidcUserInfoAuthenticationToken authentication) {
|
||||
return new Builder(authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link OidcUserInfoAuthenticationContext}.
|
||||
*/
|
||||
public static final class Builder extends AbstractBuilder<OidcUserInfoAuthenticationContext, Builder> {
|
||||
|
||||
private Builder(OidcUserInfoAuthenticationToken authentication) {
|
||||
super(authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2AccessToken OAuth 2.0 Access Token}.
|
||||
*
|
||||
* @param accessToken the {@link OAuth2AccessToken}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder accessToken(OAuth2AccessToken accessToken) {
|
||||
return put(OAuth2AccessToken.class, accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2Authorization authorization}.
|
||||
*
|
||||
* @param authorization the {@link OAuth2Authorization}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder authorization(OAuth2Authorization authorization) {
|
||||
return put(OAuth2Authorization.class, authorization);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link OidcUserInfoAuthenticationContext}.
|
||||
*
|
||||
* @return the {@link OidcUserInfoAuthenticationContext}
|
||||
*/
|
||||
public OidcUserInfoAuthenticationContext build() {
|
||||
Assert.notNull(get(OAuth2AccessToken.class), "accessToken cannot be null");
|
||||
Assert.notNull(get(OAuth2Authorization.class), "authorization cannot be null");
|
||||
return new OidcUserInfoAuthenticationContext(getContext());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,9 +29,7 @@ import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2Token;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenType;
|
||||
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
@@ -51,7 +49,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public final class OidcUserInfoAuthenticationProvider implements AuthenticationProvider {
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
private Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper = new DefaultOidcUserInfoMapper();
|
||||
private Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = new DefaultOidcUserInfoMapper();
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcUserInfoAuthenticationProvider} using the provided parameters.
|
||||
@@ -98,12 +96,11 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
Map<Object, Object> context = new HashMap<>();
|
||||
context.put(OAuth2Token.class, accessTokenAuthentication.getToken());
|
||||
context.put(OAuth2Authorization.class, authorization);
|
||||
OAuth2AuthenticationContext authenticationContext = new OAuth2AuthenticationContext(
|
||||
userInfoAuthentication, context);
|
||||
|
||||
OidcUserInfoAuthenticationContext authenticationContext =
|
||||
OidcUserInfoAuthenticationContext.with(userInfoAuthentication)
|
||||
.accessToken(authorizedAccessToken.getToken())
|
||||
.authorization(authorization)
|
||||
.build();
|
||||
OidcUserInfo userInfo = this.userInfoMapper.apply(authenticationContext);
|
||||
|
||||
return new OidcUserInfoAuthenticationToken(accessTokenAuthentication, userInfo);
|
||||
@@ -115,26 +112,26 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext}
|
||||
* Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext}
|
||||
* to an instance of {@link OidcUserInfo} for the UserInfo response.
|
||||
*
|
||||
* <p>
|
||||
* The {@link OAuth2AuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken}.
|
||||
* In addition, the following context attributes are supported:
|
||||
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken},
|
||||
* as well as, the following context attributes:
|
||||
* <ul>
|
||||
* <li>{@code OAuth2Token.class} - The {@link OAuth2Token} containing the bearer token used to make the request.</li>
|
||||
* <li>{@code OAuth2Authorization.class} - The {@link OAuth2Authorization} containing the {@link OidcIdToken} and
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and
|
||||
* {@link OAuth2AccessToken} associated with the bearer token used to make the request.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
*/
|
||||
public void setUserInfoMapper(Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper) {
|
||||
public void setUserInfoMapper(Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {
|
||||
Assert.notNull(userInfoMapper, "userInfoMapper cannot be null");
|
||||
this.userInfoMapper = userInfoMapper;
|
||||
}
|
||||
|
||||
private static final class DefaultOidcUserInfoMapper implements Function<OAuth2AuthenticationContext, OidcUserInfo> {
|
||||
private static final class DefaultOidcUserInfoMapper implements Function<OidcUserInfoAuthenticationContext, OidcUserInfo> {
|
||||
|
||||
private static final List<String> EMAIL_CLAIMS = Arrays.asList(
|
||||
StandardClaimNames.EMAIL,
|
||||
@@ -162,10 +159,10 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
);
|
||||
|
||||
@Override
|
||||
public OidcUserInfo apply(OAuth2AuthenticationContext authenticationContext) {
|
||||
OAuth2Authorization authorization = authenticationContext.get(OAuth2Authorization.class);
|
||||
public OidcUserInfo apply(OidcUserInfoAuthenticationContext authenticationContext) {
|
||||
OAuth2Authorization authorization = authenticationContext.getAuthorization();
|
||||
OidcIdToken idToken = authorization.getToken(OidcIdToken.class).getToken();
|
||||
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
|
||||
OAuth2AccessToken accessToken = authenticationContext.getAccessToken();
|
||||
Map<String, Object> scopeRequestedClaims = getClaimsRequestedByScope(idToken.getClaims(),
|
||||
accessToken.getScopes());
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.springframework.security.config.annotation.web.configuration.OAuth2Au
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
@@ -59,6 +58,7 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
@@ -230,7 +230,7 @@ public class OidcUserInfoTests {
|
||||
.getEndpointsMatcher();
|
||||
|
||||
// Custom User Info Mapper that retrieves claims from a signed JWT
|
||||
Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper = context -> {
|
||||
Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = context -> {
|
||||
OidcUserInfoAuthenticationToken authentication = context.getAuthentication();
|
||||
JwtAuthenticationToken principal = (JwtAuthenticationToken) authentication.getPrincipal();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user