@@ -180,6 +180,13 @@ public Authentication authenticate(Authentication authentication) throws Authent
180
180
throw new OAuth2AuthenticationException (OAuth2ErrorCodes .INVALID_REQUEST );
181
181
}
182
182
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
+
183
190
OAuth2Authorization actorAuthorization = null ;
184
191
if (StringUtils .hasText (tokenExchangeAuthentication .getActorToken ())) {
185
192
actorAuthorization = this .authorizationService .findByToken (
@@ -209,30 +216,22 @@ public Authentication authenticate(Authentication authentication) throws Authent
209
216
210
217
Set <String > authorizedScopes = Collections .emptySet ();
211
218
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 ());
218
222
}
219
223
220
224
if (this .logger .isTraceEnabled ()) {
221
225
this .logger .trace ("Validated token request parameters" );
222
226
}
223
227
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 );
230
229
231
230
// @formatter:off
232
231
DefaultOAuth2TokenContext .Builder tokenContextBuilder = DefaultOAuth2TokenContext .builder ()
233
232
.registeredClient (registeredClient )
234
233
.authorization (subjectAuthorization )
235
- .principal (compositePrincipal )
234
+ .principal (principal )
236
235
.authorizationServerContext (AuthorizationServerContextHolder .getContext ())
237
236
.authorizedScopes (authorizedScopes )
238
237
.tokenType (OAuth2TokenType .ACCESS_TOKEN )
@@ -262,9 +261,8 @@ public Authentication authenticate(Authentication authentication) throws Authent
262
261
.principalName (subjectAuthorization .getPrincipalName ())
263
262
.authorizationGrantType (TOKEN_EXCHANGE )
264
263
.authorizedScopes (authorizedScopes )
265
- .attribute (Principal .class .getName (), compositePrincipal );
264
+ .attribute (Principal .class .getName (), principal );
266
265
// @formatter:on
267
- // TODO: Add information about the original subject and actor authorizations?
268
266
269
267
if (generatedAccessToken instanceof ClaimAccessor ) {
270
268
authorizationBuilder .token (accessToken , (metadata ) -> {
@@ -293,15 +291,19 @@ public Authentication authenticate(Authentication authentication) throws Authent
293
291
registeredClient , clientPrincipal , accessToken , null , additionalParameters );
294
292
}
295
293
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
+ }
303
299
}
304
300
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
+
305
307
List <Authentication > actorPrincipals = new LinkedList <>();
306
308
if (actorAuthorization != null ) {
307
309
actorPrincipals .add (new OAuth2ActorAuthenticationToken (actorAuthorization .getPrincipalName ()));
@@ -317,7 +319,8 @@ private static Authentication createCompositePrincipal(OAuth2Authorization subje
317
319
// actors exist but no actor_token exists on this request?
318
320
}
319
321
320
- return new OAuth2CompositeAuthenticationToken (subjectPrincipal , actorPrincipals );
322
+ return CollectionUtils .isEmpty (actorPrincipals ) ? subjectPrincipal :
323
+ new OAuth2CompositeAuthenticationToken (subjectPrincipal , actorPrincipals );
321
324
}
322
325
323
326
@ Override
0 commit comments