Skip to content

Commit fc42ab5

Browse files
committed
Refactor validation and attributes
1 parent 4ea0310 commit fc42ab5

File tree

2 files changed

+27
-24
lines changed

2 files changed

+27
-24
lines changed

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

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,13 @@ public Authentication authenticate(Authentication authentication) throws Authent
180180
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
181181
}
182182

183+
if (subjectAuthorization.getAttribute(Principal.class.getName()) == null) {
184+
// As per https://datatracker.ietf.org/doc/html/rfc8693#section-1.1,
185+
// we require a principal to be available via the subject_token for
186+
// impersonation or delegation use cases.
187+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
188+
}
189+
183190
OAuth2Authorization actorAuthorization = null;
184191
if (StringUtils.hasText(tokenExchangeAuthentication.getActorToken())) {
185192
actorAuthorization = this.authorizationService.findByToken(
@@ -209,30 +216,22 @@ public Authentication authenticate(Authentication authentication) throws Authent
209216

210217
Set<String> authorizedScopes = Collections.emptySet();
211218
if (!CollectionUtils.isEmpty(tokenExchangeAuthentication.getScopes())) {
212-
for (String requestedScope : tokenExchangeAuthentication.getScopes()) {
213-
if (!registeredClient.getScopes().contains(requestedScope)) {
214-
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
215-
}
216-
}
217-
authorizedScopes = new LinkedHashSet<>(tokenExchangeAuthentication.getScopes());
219+
authorizedScopes = validateRequestedScopes(registeredClient, tokenExchangeAuthentication.getScopes());
220+
} else if (!CollectionUtils.isEmpty(subjectAuthorization.getAuthorizedScopes())) {
221+
authorizedScopes = validateRequestedScopes(registeredClient, subjectAuthorization.getAuthorizedScopes());
218222
}
219223

220224
if (this.logger.isTraceEnabled()) {
221225
this.logger.trace("Validated token request parameters");
222226
}
223227

224-
if (CollectionUtils.isEmpty(authorizedScopes)) {
225-
// TODO: Validate that original authorizedScopes are also configured for this client?
226-
authorizedScopes = new LinkedHashSet<>(subjectAuthorization.getAuthorizedScopes());
227-
}
228-
229-
Authentication compositePrincipal = createCompositePrincipal(subjectAuthorization, actorAuthorization);
228+
Authentication principal = getPrincipal(subjectAuthorization, actorAuthorization);
230229

231230
// @formatter:off
232231
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
233232
.registeredClient(registeredClient)
234233
.authorization(subjectAuthorization)
235-
.principal(compositePrincipal)
234+
.principal(principal)
236235
.authorizationServerContext(AuthorizationServerContextHolder.getContext())
237236
.authorizedScopes(authorizedScopes)
238237
.tokenType(OAuth2TokenType.ACCESS_TOKEN)
@@ -262,9 +261,8 @@ public Authentication authenticate(Authentication authentication) throws Authent
262261
.principalName(subjectAuthorization.getPrincipalName())
263262
.authorizationGrantType(TOKEN_EXCHANGE)
264263
.authorizedScopes(authorizedScopes)
265-
.attribute(Principal.class.getName(), compositePrincipal);
264+
.attribute(Principal.class.getName(), principal);
266265
// @formatter:on
267-
// TODO: Add information about the original subject and actor authorizations?
268266

269267
if (generatedAccessToken instanceof ClaimAccessor) {
270268
authorizationBuilder.token(accessToken, (metadata) -> {
@@ -293,15 +291,19 @@ public Authentication authenticate(Authentication authentication) throws Authent
293291
registeredClient, clientPrincipal, accessToken, null, additionalParameters);
294292
}
295293

296-
private static Authentication createCompositePrincipal(OAuth2Authorization subjectAuthorization, OAuth2Authorization actorAuthorization) {
297-
Authentication subjectPrincipal = subjectAuthorization.getAttribute(Principal.class.getName());
298-
if (subjectPrincipal == null) {
299-
// As per https://datatracker.ietf.org/doc/html/rfc8693#section-1.1,
300-
// we require a principal to be available via the subject_token for
301-
// impersonation or delegation use cases.
302-
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
294+
private static Set<String> validateRequestedScopes(RegisteredClient registeredClient, Set<String> requestedScopes) {
295+
for (String requestedScope : requestedScopes) {
296+
if (!registeredClient.getScopes().contains(requestedScope)) {
297+
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
298+
}
303299
}
304300

301+
return new LinkedHashSet<>(requestedScopes);
302+
}
303+
304+
private static Authentication getPrincipal(OAuth2Authorization subjectAuthorization, OAuth2Authorization actorAuthorization) {
305+
Authentication subjectPrincipal = subjectAuthorization.getAttribute(Principal.class.getName());
306+
305307
List<Authentication> actorPrincipals = new LinkedList<>();
306308
if (actorAuthorization != null) {
307309
actorPrincipals.add(new OAuth2ActorAuthenticationToken(actorAuthorization.getPrincipalName()));
@@ -317,7 +319,8 @@ private static Authentication createCompositePrincipal(OAuth2Authorization subje
317319
// actors exist but no actor_token exists on this request?
318320
}
319321

320-
return new OAuth2CompositeAuthenticationToken(subjectPrincipal, actorPrincipals);
322+
return CollectionUtils.isEmpty(actorPrincipals) ? subjectPrincipal :
323+
new OAuth2CompositeAuthenticationToken(subjectPrincipal, actorPrincipals);
321324
}
322325

323326
@Override

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public Jwt generate(OAuth2TokenContext context) {
162162
for (Authentication actorPrincipal : compositeAuthenticationToken.getActors()) {
163163
Map<String, Object> actClaim = new HashMap<>();
164164
actClaim.put("sub", actorPrincipal.getName());
165-
currentClaims.put("act", actClaim);
165+
currentClaims.put("act", Collections.unmodifiableMap(actClaim));
166166
currentClaims = actClaim;
167167
}
168168
});

0 commit comments

Comments
 (0)