diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java index a011a2a5de9..a5b452f47a8 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/ClientRegistration.java @@ -15,13 +15,6 @@ */ package org.springframework.security.oauth2.client.registration; -import org.springframework.security.core.SpringSecurityCoreVersion; -import org.springframework.security.oauth2.core.AuthenticationMethod; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - import java.io.Serializable; import java.util.Arrays; import java.util.Collection; @@ -31,6 +24,13 @@ import java.util.Map; import java.util.Set; +import org.springframework.security.core.SpringSecurityCoreVersion; +import org.springframework.security.oauth2.core.AuthenticationMethod; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + /** * A representation of a client registration with an OAuth 2.0 or OpenID Connect 1.0 Provider. * @@ -489,6 +489,7 @@ public ClientRegistration build() { } else { this.validateAuthorizationCodeGrantType(); } + this.validateScopes(); return this.create(); } @@ -545,5 +546,27 @@ private void validateClientCredentialsGrantType() { Assert.hasText(this.clientId, "clientId cannot be empty"); Assert.hasText(this.tokenUri, "tokenUri cannot be empty"); } + + private void validateScopes() { + if (this.scopes == null) { + return; + } + + for (String scope : this.scopes) { + Assert.isTrue(validateScope(scope), "scope \"" + scope + "\" contains invalid characters"); + } + } + + private static boolean validateScope(String scope) { + return scope == null || + scope.chars().allMatch(c -> + withinTheRangeOf(c, 0x21, 0x21) || + withinTheRangeOf(c, 0x23, 0x5B) || + withinTheRangeOf(c, 0x5D, 0x7E)); + } + + private static boolean withinTheRangeOf(int c, int min, int max) { + return c >= min && c <= max; + } } } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java index 04c0333e56a..d3886722a53 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationTests.java @@ -535,4 +535,23 @@ public void buildWhenClientCredentialsGrantTokenUriIsNullThenThrowIllegalArgumen .build() ).isInstanceOf(IllegalArgumentException.class); } + + // gh-6256 + @Test + public void buildWhenScopesContainASpaceThenThrowIllegalArgumentException() { + assertThatThrownBy(() -> + TestClientRegistrations.clientCredentials() + .scope("openid profile email") + .build() + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void buildWhenScopesContainAnInvalidCharacterThenThrowIllegalArgumentException() { + assertThatThrownBy(() -> + TestClientRegistrations.clientCredentials() + .scope("an\"invalid\"scope") + .build() + ).isInstanceOf(IllegalArgumentException.class); + } }