Skip to content

Commit 8a68648

Browse files
committed
Add extensible ClientSettings to ClientRegistration
Signed-off-by: Pranav Manglik <pranav@undreamt.in>
1 parent 14d469c commit 8a68648

File tree

2 files changed

+93
-12
lines changed

2 files changed

+93
-12
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -755,14 +755,31 @@ public static final class ClientSettings implements Serializable {
755755
@Serial
756756
private static final long serialVersionUID = 7495627155437124692L;
757757

758-
private boolean requireProofKey;
759-
760-
private ClientSettings() {
758+
private final Map<String, Object> settings;
761759

760+
private ClientSettings(Map<String, Object> settings) {
761+
this.settings = Collections.unmodifiableMap(new LinkedHashMap<>(settings));
762762
}
763763

764764
public boolean isRequireProofKey() {
765-
return this.requireProofKey;
765+
return getSetting("settings.client.require-proof-key", true);
766+
}
767+
768+
@SuppressWarnings("unchecked")
769+
public <T> @Nullable T getSetting(String name) {
770+
Assert.hasText(name, "name cannot be empty");
771+
return (T) this.settings.get(name);
772+
}
773+
774+
@SuppressWarnings("unchecked")
775+
public <T> T getSetting(String name, T defaultValue) {
776+
Assert.hasText(name, "name cannot be empty");
777+
T value = (T) this.settings.get(name);
778+
return (value != null) ? value : defaultValue;
779+
}
780+
781+
public Map<String, Object> getSettings() {
782+
return this.settings;
766783
}
767784

768785
@Override
@@ -773,17 +790,17 @@ public boolean equals(Object o) {
773790
if (!(o instanceof ClientSettings that)) {
774791
return false;
775792
}
776-
return this.requireProofKey == that.requireProofKey;
793+
return Objects.equals(this.settings, that.settings);
777794
}
778795

779796
@Override
780797
public int hashCode() {
781-
return Objects.hashCode(this.requireProofKey);
798+
return Objects.hashCode(this.settings);
782799
}
783800

784801
@Override
785802
public String toString() {
786-
return "ClientSettings{" + "requireProofKey=" + this.requireProofKey + '}';
803+
return "ClientSettings{" + "settings=" + this.settings + '}';
787804
}
788805

789806
public static Builder builder() {
@@ -792,9 +809,10 @@ public static Builder builder() {
792809

793810
public static final class Builder {
794811

795-
private boolean requireProofKey = true;
812+
private final Map<String, Object> settings = new LinkedHashMap<>();
796813

797814
private Builder() {
815+
this.settings.put("settings.client.require-proof-key", true);
798816
}
799817

800818
/**
@@ -805,14 +823,35 @@ private Builder() {
805823
* @return the {@link Builder} for further configuration
806824
*/
807825
public Builder requireProofKey(boolean requireProofKey) {
808-
this.requireProofKey = requireProofKey;
826+
return setting("settings.client.require-proof-key", requireProofKey);
827+
}
828+
829+
/**
830+
* Sets a configuration setting.
831+
* @param name the name of the setting
832+
* @param value the value of the setting
833+
* @return the {@link Builder} for further configuration
834+
*/
835+
public Builder setting(String name, Object value) {
836+
Assert.hasText(name, "name cannot be empty");
837+
Assert.notNull(value, "value cannot be null");
838+
this.settings.put(name, value);
839+
return this;
840+
}
841+
842+
/**
843+
* Sets the configuration settings.
844+
* @param settings the configuration settings
845+
* @return the {@link Builder} for further configuration
846+
*/
847+
public Builder settings(Map<String, Object> settings) {
848+
Assert.notNull(settings, "settings cannot be null");
849+
this.settings.putAll(settings);
809850
return this;
810851
}
811852

812853
public ClientSettings build() {
813-
ClientSettings clientSettings = new ClientSettings();
814-
clientSettings.requireProofKey = this.requireProofKey;
815-
return clientSettings;
854+
return new ClientSettings(this.settings);
816855
}
817856

818857
}

oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,4 +751,46 @@ private static <T> T getStaticValue(Field field, Class<T> clazz) {
751751
}
752752
}
753753

754+
@Test
755+
void buildWhenScopesHaveInvalidCharactersThenThrowException() {
756+
assertThatIllegalArgumentException().isThrownBy(() ->
757+
// @formatter:off
758+
ClientRegistration.withRegistrationId("test")
759+
.clientId("client")
760+
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
761+
.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
762+
.authorizationUri("https://provider.com/auth")
763+
.tokenUri("https://provider.com/token")
764+
.scope("read", "invalid scope ^") // space is 0x20, which is outside the valid range
765+
.build()
766+
// @formatter:on
767+
);
768+
}
769+
770+
@Test
771+
void buildWhenClientCredentialsMissingTokenUriThenThrowException() {
772+
assertThatIllegalArgumentException().isThrownBy(() ->
773+
// @formatter:off
774+
ClientRegistration.withRegistrationId("test")
775+
.clientId("client")
776+
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
777+
// Missing tokenUri
778+
.build()
779+
// @formatter:on
780+
);
781+
}
782+
783+
@Test
784+
void buildWhenValidThenSettingsAreCorrect() {
785+
// @formatter:off
786+
ClientRegistration registration = ClientRegistration.withRegistrationId("google")
787+
.clientId("my-client")
788+
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
789+
.tokenUri("https://google.com/token")
790+
.build();
791+
// @formatter:on
792+
assertThat(registration.getRegistrationId()).isEqualTo("google");
793+
assertThat(registration.getClientId()).isEqualTo("my-client");
794+
}
795+
754796
}

0 commit comments

Comments
 (0)