Skip to content

Commit 82e4f3a

Browse files
committed
Introduce OidcUserInfoAuthenticationContext
Issue gh-441
1 parent 9b60ed2 commit 82e4f3a

File tree

4 files changed

+140
-33
lines changed

4 files changed

+140
-33
lines changed

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222
import org.springframework.security.config.annotation.ObjectPostProcessor;
2323
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2424
import org.springframework.security.oauth2.core.OAuth2AccessToken;
25-
import org.springframework.security.oauth2.core.OAuth2Token;
26-
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
2725
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
2826
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
29-
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
3027
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
28+
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
3129
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationProvider;
3230
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
3331
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter;
@@ -46,7 +44,7 @@
4644
*/
4745
public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer {
4846
private RequestMatcher requestMatcher;
49-
private Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper;
47+
private Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper;
5048

5149
/**
5250
* Restrict for internal use only.
@@ -56,22 +54,22 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
5654
}
5755

5856
/**
59-
* Sets the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext}
57+
* Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext}
6058
* to an instance of {@link OidcUserInfo} for the UserInfo response.
6159
*
6260
* <p>
63-
* The {@link OAuth2AuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken}.
64-
* In addition, the following context attributes are supported:
61+
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken},
62+
* as well as, the following context attributes:
6563
* <ul>
66-
* <li>{@code OAuth2Token.class} - The {@link OAuth2Token} containing the bearer token used to make the request.</li>
67-
* <li>{@code OAuth2Authorization.class} - The {@link OAuth2Authorization} containing the {@link OidcIdToken} and
64+
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.</li>
65+
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and
6866
* {@link OAuth2AccessToken} associated with the bearer token used to make the request.</li>
6967
* </ul>
7068
*
71-
* @param userInfoMapper the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext} to an instance of {@link OidcUserInfo}
69+
* @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
7270
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
7371
*/
74-
public OidcUserInfoEndpointConfigurer userInfoMapper(Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper) {
72+
public OidcUserInfoEndpointConfigurer userInfoMapper(Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {
7573
this.userInfoMapper = userInfoMapper;
7674
return this;
7775
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright 2020-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.server.authorization.oidc.authentication;
17+
18+
import java.util.Map;
19+
import java.util.function.Function;
20+
21+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
22+
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
23+
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
24+
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
25+
import org.springframework.util.Assert;
26+
27+
/**
28+
* An {@link OAuth2AuthenticationContext} that holds an {@link OidcUserInfoAuthenticationToken} and additional information
29+
* and is used when mapping claims to an instance of {@link OidcUserInfo}.
30+
*
31+
* @author Joe Grandja
32+
* @since 0.2.1
33+
* @see OAuth2AuthenticationContext
34+
* @see OidcUserInfoAuthenticationProvider#setUserInfoMapper(Function)
35+
*/
36+
public final class OidcUserInfoAuthenticationContext extends OAuth2AuthenticationContext {
37+
38+
private OidcUserInfoAuthenticationContext(Map<Object, Object> context) {
39+
super(context);
40+
}
41+
42+
/**
43+
* Returns the {@link OAuth2AccessToken OAuth 2.0 Access Token}.
44+
*
45+
* @return the {@link OAuth2AccessToken}
46+
*/
47+
public OAuth2AccessToken getAccessToken() {
48+
return get(OAuth2AccessToken.class);
49+
}
50+
51+
/**
52+
* Returns the {@link OAuth2Authorization authorization}.
53+
*
54+
* @return the {@link OAuth2Authorization}
55+
*/
56+
public OAuth2Authorization getAuthorization() {
57+
return get(OAuth2Authorization.class);
58+
}
59+
60+
/**
61+
* Constructs a new {@link Builder} with the provided {@link OidcUserInfoAuthenticationToken}.
62+
*
63+
* @param authentication the {@link OidcUserInfoAuthenticationToken}
64+
* @return the {@link Builder}
65+
*/
66+
public static Builder with(OidcUserInfoAuthenticationToken authentication) {
67+
return new Builder(authentication);
68+
}
69+
70+
/**
71+
* A builder for {@link OidcUserInfoAuthenticationContext}.
72+
*/
73+
public static final class Builder extends AbstractBuilder<OidcUserInfoAuthenticationContext, Builder> {
74+
75+
private Builder(OidcUserInfoAuthenticationToken authentication) {
76+
super(authentication);
77+
}
78+
79+
/**
80+
* Sets the {@link OAuth2AccessToken OAuth 2.0 Access Token}.
81+
*
82+
* @param accessToken the {@link OAuth2AccessToken}
83+
* @return the {@link Builder} for further configuration
84+
*/
85+
public Builder accessToken(OAuth2AccessToken accessToken) {
86+
return put(OAuth2AccessToken.class, accessToken);
87+
}
88+
89+
/**
90+
* Sets the {@link OAuth2Authorization authorization}.
91+
*
92+
* @param authorization the {@link OAuth2Authorization}
93+
* @return the {@link Builder} for further configuration
94+
*/
95+
public Builder authorization(OAuth2Authorization authorization) {
96+
return put(OAuth2Authorization.class, authorization);
97+
}
98+
99+
/**
100+
* Builds a new {@link OidcUserInfoAuthenticationContext}.
101+
*
102+
* @return the {@link OidcUserInfoAuthenticationContext}
103+
*/
104+
public OidcUserInfoAuthenticationContext build() {
105+
Assert.notNull(get(OAuth2AccessToken.class), "accessToken cannot be null");
106+
Assert.notNull(get(OAuth2Authorization.class), "authorization cannot be null");
107+
return new OidcUserInfoAuthenticationContext(getContext());
108+
}
109+
110+
}
111+
112+
}

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

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@
2929
import org.springframework.security.oauth2.core.OAuth2AccessToken;
3030
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3131
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
32-
import org.springframework.security.oauth2.core.OAuth2Token;
3332
import org.springframework.security.oauth2.core.OAuth2TokenType;
34-
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
3533
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
3634
import org.springframework.security.oauth2.core.oidc.OidcScopes;
3735
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
@@ -51,7 +49,7 @@
5149
*/
5250
public final class OidcUserInfoAuthenticationProvider implements AuthenticationProvider {
5351
private final OAuth2AuthorizationService authorizationService;
54-
private Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper = new DefaultOidcUserInfoMapper();
52+
private Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = new DefaultOidcUserInfoMapper();
5553

5654
/**
5755
* Constructs an {@code OidcUserInfoAuthenticationProvider} using the provided parameters.
@@ -98,12 +96,11 @@ public Authentication authenticate(Authentication authentication) throws Authent
9896
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
9997
}
10098

101-
Map<Object, Object> context = new HashMap<>();
102-
context.put(OAuth2Token.class, accessTokenAuthentication.getToken());
103-
context.put(OAuth2Authorization.class, authorization);
104-
OAuth2AuthenticationContext authenticationContext = new OAuth2AuthenticationContext(
105-
userInfoAuthentication, context);
106-
99+
OidcUserInfoAuthenticationContext authenticationContext =
100+
OidcUserInfoAuthenticationContext.with(userInfoAuthentication)
101+
.accessToken(authorizedAccessToken.getToken())
102+
.authorization(authorization)
103+
.build();
107104
OidcUserInfo userInfo = this.userInfoMapper.apply(authenticationContext);
108105

109106
return new OidcUserInfoAuthenticationToken(accessTokenAuthentication, userInfo);
@@ -115,26 +112,26 @@ public boolean supports(Class<?> authentication) {
115112
}
116113

117114
/**
118-
* Sets the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext}
115+
* Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext}
119116
* to an instance of {@link OidcUserInfo} for the UserInfo response.
120117
*
121118
* <p>
122-
* The {@link OAuth2AuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken}.
123-
* In addition, the following context attributes are supported:
119+
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken},
120+
* as well as, the following context attributes:
124121
* <ul>
125-
* <li>{@code OAuth2Token.class} - The {@link OAuth2Token} containing the bearer token used to make the request.</li>
126-
* <li>{@code OAuth2Authorization.class} - The {@link OAuth2Authorization} containing the {@link OidcIdToken} and
122+
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.</li>
123+
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and
127124
* {@link OAuth2AccessToken} associated with the bearer token used to make the request.</li>
128125
* </ul>
129126
*
130-
* @param userInfoMapper the {@link Function} used to extract claims from an {@link OAuth2AuthenticationContext} to an instance of {@link OidcUserInfo}
127+
* @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
131128
*/
132-
public void setUserInfoMapper(Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper) {
129+
public void setUserInfoMapper(Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {
133130
Assert.notNull(userInfoMapper, "userInfoMapper cannot be null");
134131
this.userInfoMapper = userInfoMapper;
135132
}
136133

137-
private static final class DefaultOidcUserInfoMapper implements Function<OAuth2AuthenticationContext, OidcUserInfo> {
134+
private static final class DefaultOidcUserInfoMapper implements Function<OidcUserInfoAuthenticationContext, OidcUserInfo> {
138135

139136
private static final List<String> EMAIL_CLAIMS = Arrays.asList(
140137
StandardClaimNames.EMAIL,
@@ -162,10 +159,10 @@ private static final class DefaultOidcUserInfoMapper implements Function<OAuth2A
162159
);
163160

164161
@Override
165-
public OidcUserInfo apply(OAuth2AuthenticationContext authenticationContext) {
166-
OAuth2Authorization authorization = authenticationContext.get(OAuth2Authorization.class);
162+
public OidcUserInfo apply(OidcUserInfoAuthenticationContext authenticationContext) {
163+
OAuth2Authorization authorization = authenticationContext.getAuthorization();
167164
OidcIdToken idToken = authorization.getToken(OidcIdToken.class).getToken();
168-
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
165+
OAuth2AccessToken accessToken = authenticationContext.getAccessToken();
169166
Map<String, Object> scopeRequestedClaims = getClaimsRequestedByScope(idToken.getClaims(),
170167
accessToken.getScopes());
171168

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
3939
import org.springframework.security.config.test.SpringTestRule;
4040
import org.springframework.security.oauth2.core.OAuth2AccessToken;
41-
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
4241
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
4342
import org.springframework.security.oauth2.core.oidc.OidcScopes;
4443
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
@@ -59,6 +58,7 @@
5958
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
6059
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
6160
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
61+
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
6262
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
6363
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
6464
import org.springframework.security.web.SecurityFilterChain;
@@ -230,7 +230,7 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
230230
.getEndpointsMatcher();
231231

232232
// Custom User Info Mapper that retrieves claims from a signed JWT
233-
Function<OAuth2AuthenticationContext, OidcUserInfo> userInfoMapper = context -> {
233+
Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = context -> {
234234
OidcUserInfoAuthenticationToken authentication = context.getAuthentication();
235235
JwtAuthenticationToken principal = (JwtAuthenticationToken) authentication.getPrincipal();
236236

0 commit comments

Comments
 (0)