Skip to content

Commit

Permalink
Add ClientRegistration codeChallengeMethod to Enable PKCE
Browse files Browse the repository at this point in the history
Closes spring-projectsgh-16382

Signed-off-by: DingHao <dh.hiekn@gmail.com>
  • Loading branch information
kse-music committed Jan 9, 2025
1 parent 0e3cfd1 commit 6f682ac
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -71,6 +71,8 @@ public final class ClientRegistration implements Serializable {

private String clientName;

private String codeChallengeMethod;

private ClientRegistration() {
}

Expand Down Expand Up @@ -162,6 +164,14 @@ public String getClientName() {
return this.clientName;
}

/**
* Returns the codeChallengeMethod of the client or registration.
* @return the codeChallengeMethod
*/
public String getCodeChallengeMethod() {
return this.codeChallengeMethod;
}

@Override
public String toString() {
// @formatter:off
Expand All @@ -175,6 +185,7 @@ public String toString() {
+ '\'' + ", scopes=" + this.scopes
+ ", providerDetails=" + this.providerDetails
+ ", clientName='" + this.clientName + '\''
+ ", codeChallengeMethod='" + this.codeChallengeMethod + '\''
+ '}';
// @formatter:on
}
Expand Down Expand Up @@ -367,6 +378,8 @@ public static final class Builder implements Serializable {

private String clientName;

private String codeChallengeMethod;

private Builder(String registrationId) {
this.registrationId = registrationId;
}
Expand All @@ -391,6 +404,7 @@ private Builder(ClientRegistration clientRegistration) {
this.configurationMetadata = new HashMap<>(configurationMetadata);
}
this.clientName = clientRegistration.clientName;
this.codeChallengeMethod = clientRegistration.codeChallengeMethod;
}

/**
Expand Down Expand Up @@ -594,6 +608,16 @@ public Builder clientName(String clientName) {
return this;
}

/**
* Sets the codeChallengeMethod of the client or registration.
* @param codeChallengeMethod the codeChallengeMethod
* @return the {@link Builder}
*/
public Builder codeChallengeMethod(String codeChallengeMethod) {
this.codeChallengeMethod = codeChallengeMethod;
return this;
}

/**
* Builds a new {@link ClientRegistration}.
* @return a {@link ClientRegistration}
Expand Down Expand Up @@ -627,12 +651,13 @@ private ClientRegistration create() {
clientRegistration.providerDetails = createProviderDetails(clientRegistration);
clientRegistration.clientName = StringUtils.hasText(this.clientName) ? this.clientName
: this.registrationId;
clientRegistration.codeChallengeMethod = this.codeChallengeMethod;
return clientRegistration;
}

private ClientAuthenticationMethod deduceClientAuthenticationMethod(ClientRegistration clientRegistration) {
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(this.authorizationGrantType)
&& !StringUtils.hasText(this.clientSecret)) {
&& (!StringUtils.hasText(this.clientSecret) || StringUtils.hasText(this.codeChallengeMethod))) {
return ClientAuthenticationMethod.NONE;
}
return ClientAuthenticationMethod.CLIENT_SECRET_BASIC;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -34,6 +34,7 @@
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.web.util.UrlUtils;
Expand Down Expand Up @@ -185,6 +186,10 @@ private OAuth2AuthorizationRequest.Builder getBuilder(ClientRegistration clientR
}
if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
DEFAULT_PKCE_APPLIER.accept(builder);
if (StringUtils.hasText(clientRegistration.getCodeChallengeMethod())) {
builder.additionalParameters((params) -> params.put(PkceParameterNames.CODE_CHALLENGE_METHOD,
clientRegistration.getCodeChallengeMethod()));
}
}
return builder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -56,6 +56,8 @@ public class DefaultOAuth2AuthorizationRequestResolverTests {

private ClientRegistration registration2;

private ClientRegistration pkceClientRegistration;

private ClientRegistration fineRedirectUriTemplateRegistration;

private ClientRegistration publicClientRegistration;
Expand All @@ -72,6 +74,9 @@ public class DefaultOAuth2AuthorizationRequestResolverTests {
public void setUp() {
this.registration1 = TestClientRegistrations.clientRegistration().build();
this.registration2 = TestClientRegistrations.clientRegistration2().build();

this.pkceClientRegistration = pkceClientRegistration().build();

this.fineRedirectUriTemplateRegistration = fineRedirectUriTemplateClientRegistration().build();
// @formatter:off
this.publicClientRegistration = TestClientRegistrations.clientRegistration()
Expand All @@ -86,8 +91,8 @@ public void setUp() {
.build();
// @formatter:on
this.clientRegistrationRepository = new InMemoryClientRegistrationRepository(this.registration1,
this.registration2, this.fineRedirectUriTemplateRegistration, this.publicClientRegistration,
this.oidcRegistration);
this.registration2, this.pkceClientRegistration, this.fineRedirectUriTemplateRegistration,
this.publicClientRegistration, this.oidcRegistration);
this.resolver = new DefaultOAuth2AuthorizationRequestResolver(this.clientRegistrationRepository,
this.authorizationRequestBaseUri);
}
Expand Down Expand Up @@ -563,6 +568,32 @@ public void resolveWhenAuthorizationRequestCustomizerOverridesParameterThenQuery
+ "nonce=([a-zA-Z0-9\\-\\.\\_\\~]){43}&" + "appid=client-id");
}

@Test
public void resolveWhenAuthorizationRequestProvideCodeChallengeMethod() {
ClientRegistration clientRegistration = this.pkceClientRegistration;
String requestUri = this.authorizationRequestBaseUri + "/" + clientRegistration.getRegistrationId();
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
request.setServletPath(requestUri);
OAuth2AuthorizationRequest authorizationRequest = this.resolver.resolve(request);
assertThat(authorizationRequest.getAdditionalParameters().get(PkceParameterNames.CODE_CHALLENGE_METHOD))
.isEqualTo(clientRegistration.getCodeChallengeMethod());
}

private static ClientRegistration.Builder pkceClientRegistration() {
return ClientRegistration.withRegistrationId("pkce")
.redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}")
.codeChallengeMethod("S256")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.scope("read:user")
.authorizationUri("https://example.com/login/oauth/authorize")
.tokenUri("https://example.com/login/oauth/access_token")
.userInfoUri("https://api.example.com/user")
.userNameAttributeName("id")
.clientName("Client Name")
.clientId("client-id-3")
.clientSecret("client-secret");
}

private static ClientRegistration.Builder fineRedirectUriTemplateClientRegistration() {
// @formatter:off
return ClientRegistration.withRegistrationId("fine-redirect-uri-template-client-registration")
Expand Down

0 comments on commit 6f682ac

Please sign in to comment.