Skip to content

Commit 4b78a5e

Browse files
committed
Enforce one-time use for request_uri used in PAR
Issue gh-1925 Closes gh-1974
1 parent c82aace commit 4b78a5e

File tree

2 files changed

+66
-54
lines changed

2 files changed

+66
-54
lines changed

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

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,55 @@ public OAuth2AuthorizationCodeRequestAuthenticationProvider(RegisteredClientRepo
122122
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
123123
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;
124124

125+
OAuth2Authorization pushedAuthorization = null;
125126
String requestUri = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()
126127
.get("request_uri");
127128
if (StringUtils.hasText(requestUri)) {
128-
authorizationCodeRequestAuthentication = fromPushedAuthorizationRequest(
129-
authorizationCodeRequestAuthentication);
129+
OAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = null;
130+
try {
131+
pushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri.parse(requestUri);
132+
}
133+
catch (Exception ex) {
134+
throwError(OAuth2ErrorCodes.INVALID_REQUEST, "request_uri", authorizationCodeRequestAuthentication,
135+
null);
136+
}
137+
138+
pushedAuthorization = this.authorizationService.findByToken(pushedAuthorizationRequestUri.getState(),
139+
STATE_TOKEN_TYPE);
140+
if (pushedAuthorization == null) {
141+
throwError(OAuth2ErrorCodes.INVALID_REQUEST, "request_uri", authorizationCodeRequestAuthentication,
142+
null);
143+
}
144+
145+
if (this.logger.isTraceEnabled()) {
146+
this.logger.trace("Retrieved authorization with pushed authorization request");
147+
}
148+
149+
OAuth2AuthorizationRequest authorizationRequest = pushedAuthorization
150+
.getAttribute(OAuth2AuthorizationRequest.class.getName());
151+
152+
if (!authorizationCodeRequestAuthentication.getClientId().equals(authorizationRequest.getClientId())) {
153+
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID,
154+
authorizationCodeRequestAuthentication, null);
155+
}
156+
157+
if (Instant.now().isAfter(pushedAuthorizationRequestUri.getExpiresAt())) {
158+
// Remove (effectively invalidating) the pushed authorization request
159+
this.authorizationService.remove(pushedAuthorization);
160+
if (this.logger.isWarnEnabled()) {
161+
this.logger
162+
.warn(LogMessage.format("Removed expired pushed authorization request for client id '%s'",
163+
authorizationRequest.getClientId()));
164+
}
165+
throwError(OAuth2ErrorCodes.INVALID_REQUEST, "request_uri", authorizationCodeRequestAuthentication,
166+
null);
167+
}
168+
169+
authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(
170+
authorizationCodeRequestAuthentication.getAuthorizationUri(), authorizationRequest.getClientId(),
171+
(Authentication) authorizationCodeRequestAuthentication.getPrincipal(),
172+
authorizationRequest.getRedirectUri(), authorizationRequest.getState(),
173+
authorizationRequest.getScopes(), authorizationRequest.getAdditionalParameters());
130174
}
131175

132176
RegisteredClient registeredClient = this.registeredClientRepository
@@ -223,13 +267,21 @@ public Authentication authenticate(Authentication authentication) throws Authent
223267

224268
this.authorizationService.save(authorization);
225269

226-
Set<String> currentAuthorizedScopes = (currentAuthorizationConsent != null)
227-
? currentAuthorizationConsent.getScopes() : null;
228-
229270
if (this.logger.isTraceEnabled()) {
230271
this.logger.trace("Saved authorization");
231272
}
232273

274+
if (pushedAuthorization != null) {
275+
// Enforce one-time use by removing the pushed authorization request
276+
this.authorizationService.remove(pushedAuthorization);
277+
if (this.logger.isTraceEnabled()) {
278+
this.logger.trace("Removed authorization with pushed authorization request");
279+
}
280+
}
281+
282+
Set<String> currentAuthorizedScopes = (currentAuthorizationConsent != null)
283+
? currentAuthorizationConsent.getScopes() : null;
284+
233285
return new OAuth2AuthorizationConsentAuthenticationToken(authorizationRequest.getAuthorizationUri(),
234286
registeredClient.getClientId(), principal, state, currentAuthorizedScopes, null);
235287
}
@@ -257,6 +309,14 @@ public Authentication authenticate(Authentication authentication) throws Authent
257309
this.logger.trace("Saved authorization");
258310
}
259311

312+
if (pushedAuthorization != null) {
313+
// Enforce one-time use by removing the pushed authorization request
314+
this.authorizationService.remove(pushedAuthorization);
315+
if (this.logger.isTraceEnabled()) {
316+
this.logger.trace("Removed authorization with pushed authorization request");
317+
}
318+
}
319+
260320
String redirectUri = authorizationRequest.getRedirectUri();
261321
if (!StringUtils.hasText(redirectUri)) {
262322
redirectUri = registeredClient.getRedirectUris().iterator().next();
@@ -335,55 +395,6 @@ public void setAuthorizationConsentRequired(
335395
this.authorizationConsentRequired = authorizationConsentRequired;
336396
}
337397

338-
private OAuth2AuthorizationCodeRequestAuthenticationToken fromPushedAuthorizationRequest(
339-
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {
340-
341-
String requestUri = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()
342-
.get("request_uri");
343-
344-
OAuth2PushedAuthorizationRequestUri pushedAuthorizationRequestUri = null;
345-
try {
346-
pushedAuthorizationRequestUri = OAuth2PushedAuthorizationRequestUri.parse(requestUri);
347-
}
348-
catch (Exception ex) {
349-
throwError(OAuth2ErrorCodes.INVALID_REQUEST, "request_uri", authorizationCodeRequestAuthentication, null);
350-
}
351-
352-
OAuth2Authorization authorization = this.authorizationService
353-
.findByToken(pushedAuthorizationRequestUri.getState(), STATE_TOKEN_TYPE);
354-
if (authorization == null) {
355-
throwError(OAuth2ErrorCodes.INVALID_REQUEST, "request_uri", authorizationCodeRequestAuthentication, null);
356-
}
357-
358-
if (this.logger.isTraceEnabled()) {
359-
this.logger.trace("Retrieved authorization with pushed authorization request");
360-
}
361-
362-
OAuth2AuthorizationRequest authorizationRequest = authorization
363-
.getAttribute(OAuth2AuthorizationRequest.class.getName());
364-
365-
if (!authorizationCodeRequestAuthentication.getClientId().equals(authorizationRequest.getClientId())) {
366-
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID,
367-
authorizationCodeRequestAuthentication, null);
368-
}
369-
370-
if (Instant.now().isAfter(pushedAuthorizationRequestUri.getExpiresAt())) {
371-
// Remove (effectively invalidating) the pushed authorization request
372-
this.authorizationService.remove(authorization);
373-
if (this.logger.isWarnEnabled()) {
374-
this.logger.warn(LogMessage.format("Removed expired pushed authorization request for client id '%s'",
375-
authorizationRequest.getClientId()));
376-
}
377-
throwError(OAuth2ErrorCodes.INVALID_REQUEST, "request_uri", authorizationCodeRequestAuthentication, null);
378-
}
379-
380-
return new OAuth2AuthorizationCodeRequestAuthenticationToken(
381-
authorizationCodeRequestAuthentication.getAuthorizationUri(), authorizationRequest.getClientId(),
382-
(Authentication) authorizationCodeRequestAuthentication.getPrincipal(),
383-
authorizationRequest.getRedirectUri(), authorizationRequest.getState(),
384-
authorizationRequest.getScopes(), authorizationRequest.getAdditionalParameters());
385-
}
386-
387398
private static boolean isAuthorizationConsentRequired(
388399
OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
389400
if (!authenticationContext.getRegisteredClient().getClientSettings().isRequireAuthorizationConsent()) {

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProviderTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@ public void authenticateWhenAuthorizationCodeRequestWithRequestUriThenReturnAuth
633633

634634
assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication,
635635
authenticationResult);
636+
verify(this.authorizationService).remove(eq(authorization));
636637
}
637638

638639
@Test

0 commit comments

Comments
 (0)