Description
PKCE is recommended to prevent CSRF and authorization code injection attacks. We should further simplify enabling PKCE for Confidential Clients.
Current Behavior
The simplest Spring Boot application that requires PKCE is shown below:
@Bean
SecurityFilterChain springSecurity(HttpSecurity http, OAuth2AuthorizationRequestResolver resolver) throws Exception {
http
.authorizeHttpRequests( r -> r
.anyRequest().authenticated()
)
.oauth2Login(l -> l
.authorizationEndpoint(a -> a
.authorizationRequestResolver(resolver)
)
);
return http.build();
}
@Bean
OAuth2AuthorizationRequestResolver pkceResolver(ClientRegistrationRepository clientRegistrationRepository) {
DefaultOAuth2AuthorizationRequestResolver resolver =
new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,"/oauth2/authorization");
resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce());
return resolver;
}
Shortcomings
This will be simplified to just a Bean declaration when gh-16380 is implemented, but this still has the following downsides:
- It is not discoverable from the configuration of a typical Spring Security application
- It is not declarative
For something that is considered a best security practice (rather than an edge case), we must do better.
Solution
I think what makes the most sense is adding a property ClientRegistration.clientSettings.requireProofKey=(true|false)
(default is false) similar to what was proposed in gh-12219.
UPDATE After speaking with @jgrandja offline, he suggested the property ClientRegistration.clientSettings.requireProofKey=(true|false)
to align with Autorization Server's RegisteredClient.[clientSettings](https://github.com/spring-projects/spring-authorization-server/blob/1.4.1/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java#L73).requireProofKey
. In this case, we can introduce the ClientSettings
in the same package as ClientRegistration
and it does NOT need to be backed by a Map
or extend AbstractSettings
. If needed, we can later introduce AbstractSettings
and back it by a Map
at that point.
Note that this is on the ClientRegistration
because this is how Spring Security decides which grant type to use. If a single application has multiple grant types for the same provider (or different providers), then it is possible that PKCE is not even valid for a specific registration.
This also has the advantage of making it simple for users to define a property using Spring Boot to declaratively enable PKCE with Spring Security.
Previous Discussions
Perhaps this is no longer a problem, but I wanted to bring it up proactively. I know that this was previously declined because:
- PKCE is only applicable for
authorization_code
, so there was hesitation around introducing a PKCE property. I do not find this a valid argument. Spring Security already has properties that are only used for specific grant types and custom validation based upon the grant type. For example, theauthorizationUri
andtokenUri
only make sense for theauthorization_code
grant type and not forclient_credentials
. A property for enabling PKCE is just another property, that is validated differently by the grant type. - Enabling PKCE makes more sense with Spring Boot auto-configuration. However, Spring Boot rejected the proposal.
- There are concerns that PKCE may not be something that is configured per client. I'd argue that it needs to be per registration because this is how Spring Security decides which grant type to use. If a single application has multiple grant types for the same provider (or different providers), then it is possible that PKCE is not even valid for a specific registration.