1
1
/*
2
- * Copyright 2020-2024 the original author or authors.
2
+ * Copyright 2020-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
27
27
import org .apache .commons .logging .Log ;
28
28
import org .apache .commons .logging .LogFactory ;
29
29
30
- import org .springframework .core .log .LogMessage ;
31
30
import org .springframework .security .authentication .AnonymousAuthenticationToken ;
32
31
import org .springframework .security .authentication .AuthenticationProvider ;
33
32
import org .springframework .security .core .Authentication ;
39
38
import org .springframework .security .oauth2 .core .OAuth2ErrorCodes ;
40
39
import org .springframework .security .oauth2 .core .endpoint .OAuth2AuthorizationRequest ;
41
40
import org .springframework .security .oauth2 .core .endpoint .OAuth2ParameterNames ;
42
- import org .springframework .security .oauth2 .core .endpoint .PkceParameterNames ;
43
41
import org .springframework .security .oauth2 .core .oidc .OidcScopes ;
44
42
import org .springframework .security .oauth2 .server .authorization .OAuth2Authorization ;
45
43
import org .springframework .security .oauth2 .server .authorization .OAuth2AuthorizationCode ;
@@ -81,8 +79,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
81
79
82
80
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1" ;
83
81
84
- private static final String PKCE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1" ;
85
-
86
82
private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator (
87
83
Base64 .getUrlEncoder ());
88
84
@@ -122,6 +118,13 @@ public OAuth2AuthorizationCodeRequestAuthenticationProvider(RegisteredClientRepo
122
118
public Authentication authenticate (Authentication authentication ) throws AuthenticationException {
123
119
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = (OAuth2AuthorizationCodeRequestAuthenticationToken ) authentication ;
124
120
121
+ String requestUri = (String ) authorizationCodeRequestAuthentication .getAdditionalParameters ()
122
+ .get ("request_uri" );
123
+ if (StringUtils .hasText (requestUri )) {
124
+ authorizationCodeRequestAuthentication = loadPushedAuthorizationRequest (
125
+ authorizationCodeRequestAuthentication , requestUri );
126
+ }
127
+
125
128
RegisteredClient registeredClient = this .registeredClientRepository
126
129
.findByClientId (authorizationCodeRequestAuthentication .getClientId ());
127
130
if (registeredClient == null ) {
@@ -136,47 +139,28 @@ public Authentication authenticate(Authentication authentication) throws Authent
136
139
OAuth2AuthorizationCodeRequestAuthenticationContext .Builder authenticationContextBuilder = OAuth2AuthorizationCodeRequestAuthenticationContext
137
140
.with (authorizationCodeRequestAuthentication )
138
141
.registeredClient (registeredClient );
139
- this .authenticationValidator .accept (authenticationContextBuilder .build ());
142
+ OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext = authenticationContextBuilder
143
+ .build ();
140
144
141
- if (!registeredClient .getAuthorizationGrantTypes ().contains (AuthorizationGrantType .AUTHORIZATION_CODE )) {
142
- if (this .logger .isDebugEnabled ()) {
143
- this .logger .debug (LogMessage .format (
144
- "Invalid request: requested grant_type is not allowed" + " for registered client '%s'" ,
145
- registeredClient .getId ()));
146
- }
147
- throwError (OAuth2ErrorCodes .UNAUTHORIZED_CLIENT , OAuth2ParameterNames .CLIENT_ID ,
148
- authorizationCodeRequestAuthentication , registeredClient );
149
- }
145
+ // grant_type
146
+ OAuth2AuthorizationCodeRequestAuthenticationValidator .DEFAULT_AUTHORIZATION_GRANT_TYPE_VALIDATOR
147
+ .accept (authenticationContext );
148
+
149
+ // scope and redirect_uri
150
+ this .authenticationValidator .accept (authenticationContext );
150
151
151
152
// code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE)
152
- String codeChallenge = (String ) authorizationCodeRequestAuthentication .getAdditionalParameters ()
153
- .get (PkceParameterNames .CODE_CHALLENGE );
154
- if (StringUtils .hasText (codeChallenge )) {
155
- String codeChallengeMethod = (String ) authorizationCodeRequestAuthentication .getAdditionalParameters ()
156
- .get (PkceParameterNames .CODE_CHALLENGE_METHOD );
157
- if (!StringUtils .hasText (codeChallengeMethod ) || !"S256" .equals (codeChallengeMethod )) {
158
- throwError (OAuth2ErrorCodes .INVALID_REQUEST , PkceParameterNames .CODE_CHALLENGE_METHOD , PKCE_ERROR_URI ,
159
- authorizationCodeRequestAuthentication , registeredClient , null );
160
- }
161
- }
162
- else if (registeredClient .getClientSettings ().isRequireProofKey ()) {
163
- throwError (OAuth2ErrorCodes .INVALID_REQUEST , PkceParameterNames .CODE_CHALLENGE , PKCE_ERROR_URI ,
164
- authorizationCodeRequestAuthentication , registeredClient , null );
165
- }
153
+ OAuth2AuthorizationCodeRequestAuthenticationValidator .DEFAULT_CODE_CHALLENGE_VALIDATOR
154
+ .accept (authenticationContext );
166
155
167
156
// prompt (OPTIONAL for OpenID Connect 1.0 Authentication Request)
168
157
Set <String > promptValues = Collections .emptySet ();
169
158
if (authorizationCodeRequestAuthentication .getScopes ().contains (OidcScopes .OPENID )) {
170
159
String prompt = (String ) authorizationCodeRequestAuthentication .getAdditionalParameters ().get ("prompt" );
171
160
if (StringUtils .hasText (prompt )) {
161
+ OAuth2AuthorizationCodeRequestAuthenticationValidator .DEFAULT_PROMPT_VALIDATOR
162
+ .accept (authenticationContext );
172
163
promptValues = new HashSet <>(Arrays .asList (StringUtils .delimitedListToStringArray (prompt , " " )));
173
- if (promptValues .contains (OidcPrompts .NONE )) {
174
- if (promptValues .contains (OidcPrompts .LOGIN ) || promptValues .contains (OidcPrompts .CONSENT )
175
- || promptValues .contains (OidcPrompts .SELECT_ACCOUNT )) {
176
- throwError (OAuth2ErrorCodes .INVALID_REQUEST , "prompt" , authorizationCodeRequestAuthentication ,
177
- registeredClient );
178
- }
179
- }
180
164
}
181
165
}
182
166
@@ -283,6 +267,32 @@ else if (registeredClient.getClientSettings().isRequireProofKey()) {
283
267
authorizationRequest .getState (), authorizationRequest .getScopes ());
284
268
}
285
269
270
+ private OAuth2AuthorizationCodeRequestAuthenticationToken loadPushedAuthorizationRequest (
271
+ OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication ,
272
+ String requestUri ) {
273
+
274
+ final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType (OAuth2ParameterNames .STATE );
275
+
276
+ String state = requestUri .replace ("urn:ietf:params:oauth:request_uri:" , "" );
277
+
278
+ OAuth2Authorization authorization = this .authorizationService .findByToken (state , STATE_TOKEN_TYPE );
279
+ if (authorization == null ) {
280
+ throwError (OAuth2ErrorCodes .INVALID_REQUEST , "request_uri" , authorizationCodeRequestAuthentication , null );
281
+ }
282
+
283
+ OAuth2AuthorizationRequest authorizationRequest = authorization
284
+ .getAttribute (OAuth2AuthorizationRequest .class .getName ());
285
+
286
+ OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken (
287
+ authorizationCodeRequestAuthentication .getAuthorizationUri (),
288
+ authorizationCodeRequestAuthentication .getClientId (),
289
+ (Authentication ) authorizationCodeRequestAuthentication .getPrincipal (),
290
+ authorizationRequest .getRedirectUri (), authorizationRequest .getState (),
291
+ authorizationRequest .getScopes (), authorizationRequest .getAdditionalParameters ());
292
+
293
+ return authorizationCodeRequestAuthenticationResult ;
294
+ }
295
+
286
296
@ Override
287
297
public boolean supports (Class <?> authentication ) {
288
298
return OAuth2AuthorizationCodeRequestAuthenticationToken .class .isAssignableFrom (authentication );
@@ -457,23 +467,4 @@ private static String resolveRedirectUri(
457
467
return null ;
458
468
}
459
469
460
- /*
461
- * The values defined for the "prompt" parameter for the OpenID Connect 1.0
462
- * Authentication Request.
463
- */
464
- private static final class OidcPrompts {
465
-
466
- private static final String NONE = "none" ;
467
-
468
- private static final String LOGIN = "login" ;
469
-
470
- private static final String CONSENT = "consent" ;
471
-
472
- private static final String SELECT_ACCOUNT = "select_account" ;
473
-
474
- private OidcPrompts () {
475
- }
476
-
477
- }
478
-
479
470
}
0 commit comments