Browse Source

Introduce OidcUserInfoAuthenticationContext

Issue gh-441
pull/490/head
Joe Grandja 4 years ago
parent
commit
82e4f3a345
  1. 20
      oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcUserInfoEndpointConfigurer.java
  2. 112
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationContext.java
  3. 37
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java
  4. 4
      oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcUserInfoTests.java

20
oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcUserInfoEndpointConfigurer.java

@ -22,12 +22,10 @@ import org.springframework.security.authentication.AuthenticationManager; @@ -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; @@ -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 @@ -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;
}

112
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationContext.java

@ -0,0 +1,112 @@ @@ -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());
}
}
}

37
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java

@ -29,9 +29,7 @@ import org.springframework.security.core.AuthenticationException; @@ -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; @@ -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 @@ -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 @@ -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 @@ -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());

4
oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcUserInfoTests.java

@ -38,7 +38,6 @@ import org.springframework.security.config.annotation.web.configuration.OAuth2Au @@ -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 @@ -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 { @@ -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();

Loading…
Cancel
Save