Skip to content

Commit 06718e0

Browse files
author
Steve Riesenberg
committed
Customize OAuth2AuthorizationConsent prior to saving
Closes spring-projectsgh-436
1 parent 72c5e24 commit 06718e0

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import org.springframework.security.authentication.AnonymousAuthenticationToken;
3131
import org.springframework.security.authentication.AuthenticationProvider;
32+
import org.springframework.security.config.Customizer;
3233
import org.springframework.security.core.Authentication;
3334
import org.springframework.security.core.AuthenticationException;
3435
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
@@ -82,6 +83,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
8283
private final OAuth2AuthorizationConsentService authorizationConsentService;
8384
private Supplier<String> authorizationCodeGenerator = DEFAULT_AUTHORIZATION_CODE_GENERATOR::generateKey;
8485
private Function<String, OAuth2AuthenticationValidator> authenticationValidatorResolver = DEFAULT_AUTHENTICATION_VALIDATOR_RESOLVER;
86+
private Customizer<OAuth2AuthenticationContext> authorizationConsentCustomizer;
8587

8688
/**
8789
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationProvider} using the provided parameters.
@@ -145,6 +147,31 @@ public void setAuthenticationValidatorResolver(Function<String, OAuth2Authentica
145147
this.authenticationValidatorResolver = authenticationValidatorResolver;
146148
}
147149

150+
/**
151+
* Sets the {@link Customizer} providing access to the {@link OAuth2AuthenticationContext} containing an
152+
* {@link OAuth2AuthorizationConsent.Builder}.
153+
*
154+
* <p>
155+
* The {@link OAuth2AuthenticationContext} gives the mapper access to the
156+
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
157+
* In addition, the following context attributes are supported:
158+
* <ul>
159+
* <li>{@code OAuth2AuthorizationConsent.Builder.class} - The {@link OAuth2AuthorizationConsent.Builder} used to build the
160+
* authorization consent prior to {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}</li>
161+
* <li>{@code OAuth2Authorization.class} - The {@link OAuth2Authorization} associated with the state token presented
162+
* in the authorization consent request.</li>
163+
* <li>{@code OAuth2AuthorizationRequest.class} - The {@link OAuth2AuthorizationRequest} requiring the resource
164+
* owner's consent.</li>
165+
* </ul>
166+
*
167+
* @param authorizationConsentCustomizer the {@link Customizer} providing access to the
168+
* {@link OAuth2AuthenticationContext} containing an {@link OAuth2AuthorizationConsent.Builder}
169+
*/
170+
public void setAuthorizationConsentCustomizer(Customizer<OAuth2AuthenticationContext> authorizationConsentCustomizer) {
171+
Assert.notNull(authorizationConsentCustomizer, "authorizationConsentCustomizer cannot be null");
172+
this.authorizationConsentCustomizer = authorizationConsentCustomizer;
173+
}
174+
148175
private Authentication authenticateAuthorizationRequest(Authentication authentication) throws AuthenticationException {
149176
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
150177
(OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;
@@ -330,6 +357,17 @@ private Authentication authenticateAuthorizationConsent(Authentication authentic
330357
authorization.getRegisteredClientId(), authorization.getPrincipalName());
331358
}
332359
authorizedScopes.forEach(authorizationConsentBuilder::scope);
360+
361+
if (this.authorizationConsentCustomizer != null) {
362+
Map<Object, Object> context = new HashMap<>();
363+
context.put(OAuth2AuthorizationConsent.Builder.class, authorizationConsentBuilder);
364+
context.put(OAuth2Authorization.class, authorization);
365+
context.put(OAuth2AuthorizationRequest.class, authorizationRequest);
366+
OAuth2AuthenticationContext authenticationContext = new OAuth2AuthenticationContext(
367+
authorizationCodeRequestAuthentication, context);
368+
this.authorizationConsentCustomizer.customize(authenticationContext);
369+
}
370+
333371
OAuth2AuthorizationConsent authorizationConsent = authorizationConsentBuilder.build();
334372
this.authorizationConsentService.save(authorizationConsent);
335373
}

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

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,21 @@
2929
import org.mockito.ArgumentCaptor;
3030

3131
import org.springframework.security.authentication.TestingAuthenticationToken;
32+
import org.springframework.security.config.Customizer;
3233
import org.springframework.security.core.Authentication;
3334
import org.springframework.security.oauth2.core.AuthorizationGrantType;
35+
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
3436
import org.springframework.security.oauth2.core.OAuth2Error;
3537
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
3638
import org.springframework.security.oauth2.core.OAuth2TokenType;
39+
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationContext;
3740
import org.springframework.security.oauth2.core.authentication.OAuth2AuthenticationValidator;
3841
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
3942
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
4043
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
4144
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
4245
import org.springframework.security.oauth2.core.oidc.OidcScopes;
4346
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
44-
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
4547
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
4648
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
4749
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
@@ -129,6 +131,13 @@ public void setAuthenticationValidatorResolverWhenNullThenThrowIllegalArgumentEx
129131
.hasMessage("authenticationValidatorResolver cannot be null");
130132
}
131133

134+
@Test
135+
public void setAuthorizationConsentCustomizerWhenNullThenThrowIllegalArgumentException() {
136+
assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationConsentCustomizer(null))
137+
.isInstanceOf(IllegalArgumentException.class)
138+
.hasMessage("authorizationConsentCustomizer cannot be null");
139+
}
140+
132141
@Test
133142
public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
134143
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
@@ -773,6 +782,53 @@ public void authenticateWhenConsentRequestApproveAllThenReturnAuthorizationCode(
773782
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
774783
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
775784

785+
assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult);
786+
}
787+
788+
@Test
789+
public void authenticateWhenCustomAuthorizationConsentCustomizerThenUsed() {
790+
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
791+
.build();
792+
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
793+
.thenReturn(registeredClient);
794+
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
795+
.principalName(this.principal.getName())
796+
.build();
797+
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
798+
Set<String> authorizedScopes = authorizationRequest.getScopes();
799+
OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
800+
authorizationConsentRequestAuthentication(registeredClient, this.principal)
801+
.scopes(authorizedScopes) // Approve all scopes
802+
.build();
803+
when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE)))
804+
.thenReturn(authorization);
805+
806+
@SuppressWarnings("unchecked")
807+
Customizer<OAuth2AuthenticationContext> authorizationConsentCustomizer = mock(Customizer.class);
808+
this.authenticationProvider.setAuthorizationConsentCustomizer(authorizationConsentCustomizer);
809+
810+
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult =
811+
(OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication);
812+
813+
assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult);
814+
815+
ArgumentCaptor<OAuth2AuthenticationContext> contextCaptor = ArgumentCaptor.forClass(OAuth2AuthenticationContext.class);
816+
verify(authorizationConsentCustomizer).customize(contextCaptor.capture());
817+
818+
OAuth2AuthenticationContext context = contextCaptor.getValue();
819+
assertThat((Authentication) context.getAuthentication()).isEqualTo(authentication);
820+
assertThat(context.get(OAuth2AuthorizationConsent.Builder.class)).isInstanceOf(OAuth2AuthorizationConsent.Builder.class);
821+
assertThat(context.get(OAuth2Authorization.class)).isInstanceOf(OAuth2Authorization.class);
822+
assertThat(context.get(OAuth2AuthorizationRequest.class)).isInstanceOf(OAuth2AuthorizationRequest.class);
823+
}
824+
825+
private void assertAuthorizationConsentRequestWithAuthorizationCodeResult(
826+
RegisteredClient registeredClient,
827+
OAuth2Authorization authorization,
828+
OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) {
829+
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
830+
Set<String> authorizedScopes = authorizationRequest.getScopes();
831+
776832
ArgumentCaptor<OAuth2AuthorizationConsent> authorizationConsentCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationConsent.class);
777833
verify(this.authorizationConsentService).save(authorizationConsentCaptor.capture());
778834
OAuth2AuthorizationConsent authorizationConsent = authorizationConsentCaptor.getValue();

0 commit comments

Comments
 (0)