Merge branch 0.4.x into main
The following commits are merged using the default merge strategy.8c78a5bdeeDocument Authorization Request Validationa9371e918aDocument Jwt Client Assertion Validationfcbb5c1197Polish OAuth2AuthorizationCodeGrantTests
This commit is contained in:
@@ -253,3 +253,55 @@ The supported client authentication methods are `client_secret_basic`, `client_s
|
||||
* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `JwtClientAssertionAuthenticationProvider`, `ClientSecretAuthenticationProvider`, and `PublicClientAuthenticationProvider`.
|
||||
* `*AuthenticationSuccessHandler*` -- An internal implementation that associates the "`authenticated`" `OAuth2ClientAuthenticationToken` (current `Authentication`) to the `SecurityContext`.
|
||||
* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthenticationException` to return the OAuth2 error response.
|
||||
|
||||
[[configuring-client-authentication-customizing-jwt-client-assertion-validation]]
|
||||
=== Customizing Jwt Client Assertion Validation
|
||||
|
||||
`JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY` is the default factory that provides an `OAuth2TokenValidator<Jwt>` for the specified `RegisteredClient` and is used for validating the `iss`, `sub`, `aud`, `exp` and `nbf` claims of the `Jwt` client assertion.
|
||||
|
||||
`JwtClientAssertionDecoderFactory` provides the ability to override the default `Jwt` client assertion validation by supplying a custom factory of type `Function<RegisteredClient, OAuth2TokenValidator<Jwt>>` to `setJwtValidatorFactory()`.
|
||||
|
||||
[NOTE]
|
||||
`JwtClientAssertionDecoderFactory` is the default `JwtDecoderFactory` used by `JwtClientAssertionAuthenticationProvider` that provides a `JwtDecoder` for the specified `RegisteredClient` and is used for authenticating a `Jwt` Bearer Token during OAuth2 client authentication.
|
||||
|
||||
A common use case for customizing `JwtClientAssertionDecoderFactory` is to validate additional claims in the `Jwt` client assertion.
|
||||
|
||||
The following example shows how to configure `JwtClientAssertionAuthenticationProvider` with a customized `JwtClientAssertionDecoderFactory` that validates an additional claim in the `Jwt` client assertion:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
|
||||
new OAuth2AuthorizationServerConfigurer();
|
||||
http.apply(authorizationServerConfigurer);
|
||||
|
||||
authorizationServerConfigurer
|
||||
.clientAuthentication(clientAuthentication ->
|
||||
clientAuthentication
|
||||
.authenticationProviders(configureJwtClientAssertionValidator())
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> configureJwtClientAssertionValidator() {
|
||||
return (authenticationProviders) ->
|
||||
authenticationProviders.forEach((authenticationProvider) -> {
|
||||
if (authenticationProvider instanceof JwtClientAssertionAuthenticationProvider) {
|
||||
// Customize JwtClientAssertionDecoderFactory
|
||||
JwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory();
|
||||
Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = (registeredClient) ->
|
||||
new DelegatingOAuth2TokenValidator<>(
|
||||
// Use default validators
|
||||
JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY.apply(registeredClient),
|
||||
// Add custom validator
|
||||
new JwtClaimValidator<>("claim", "value"::equals));
|
||||
jwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);
|
||||
|
||||
((JwtClientAssertionAuthenticationProvider) authenticationProvider)
|
||||
.setJwtDecoderFactory(jwtDecoderFactory);
|
||||
}
|
||||
});
|
||||
}
|
||||
----
|
||||
|
||||
@@ -50,6 +50,76 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
|
||||
* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an "`authenticated`" `OAuth2AuthorizationCodeRequestAuthenticationToken` and returns the `OAuth2AuthorizationResponse`.
|
||||
* `*AuthenticationFailureHandler*` -- An internal implementation that uses the `OAuth2Error` associated with the `OAuth2AuthorizationCodeRequestAuthenticationException` and returns the `OAuth2Error` response.
|
||||
|
||||
[[oauth2-authorization-endpoint-customizing-authorization-request-validation]]
|
||||
=== Customizing Authorization Request Validation
|
||||
|
||||
`OAuth2AuthorizationCodeRequestAuthenticationValidator` is the default validator used for validating specific OAuth2 authorization request parameters used in the Authorization Code Grant.
|
||||
The default implementation validates the `redirect_uri` and `scope` parameters.
|
||||
If validation fails, an `OAuth2AuthorizationCodeRequestAuthenticationException` is thrown.
|
||||
|
||||
`OAuth2AuthorizationCodeRequestAuthenticationProvider` provides the ability to override the default authorization request validation by supplying a custom authentication validator of type `Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext>` to `setAuthenticationValidator()`.
|
||||
|
||||
[TIP]
|
||||
`OAuth2AuthorizationCodeRequestAuthenticationContext` holds the `OAuth2AuthorizationCodeRequestAuthenticationToken`, which contains the OAuth2 authorization request parameters.
|
||||
|
||||
[IMPORTANT]
|
||||
If validation fails, the authentication validator *MUST* throw `OAuth2AuthorizationCodeRequestAuthenticationException`.
|
||||
|
||||
A common use case during the development life cycle phase is to allow for `localhost` in the `redirect_uri` parameter.
|
||||
|
||||
The following example shows how to configure `OAuth2AuthorizationCodeRequestAuthenticationProvider` with a custom authentication validator that allows for `localhost` in the `redirect_uri` parameter:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
|
||||
new OAuth2AuthorizationServerConfigurer();
|
||||
http.apply(authorizationServerConfigurer);
|
||||
|
||||
authorizationServerConfigurer
|
||||
.authorizationEndpoint(authorizationEndpoint ->
|
||||
authorizationEndpoint
|
||||
.authenticationProviders(configureAuthenticationValidator())
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> configureAuthenticationValidator() {
|
||||
return (authenticationProviders) ->
|
||||
authenticationProviders.forEach((authenticationProvider) -> {
|
||||
if (authenticationProvider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider) {
|
||||
Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator =
|
||||
// Override default redirect_uri validator
|
||||
new CustomRedirectUriValidator()
|
||||
// Reuse default scope validator
|
||||
.andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_SCOPE_VALIDATOR);
|
||||
|
||||
((OAuth2AuthorizationCodeRequestAuthenticationProvider) authenticationProvider)
|
||||
.setAuthenticationValidator(authenticationValidator);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class CustomRedirectUriValidator implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {
|
||||
|
||||
@Override
|
||||
public void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authenticationContext.getAuthentication();
|
||||
RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
|
||||
String requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri();
|
||||
|
||||
// Use exact string matching when comparing client redirect URIs against pre-registered URIs
|
||||
if (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[oauth2-token-endpoint]]
|
||||
== OAuth2 Token Endpoint
|
||||
|
||||
|
||||
@@ -943,11 +943,6 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
@EnableWebSecurity
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class AuthorizationServerConfigurationCustomConsentRequest extends AuthorizationServerConfiguration {
|
||||
@Autowired
|
||||
private RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
@Autowired
|
||||
private OAuth2AuthorizationService authorizationService;
|
||||
|
||||
@Autowired
|
||||
private OAuth2AuthorizationConsentService authorizationConsentService;
|
||||
|
||||
Reference in New Issue
Block a user