/* * Copyright 2020-2021 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. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.oauth2.server.authorization.client; import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Test; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Tests for {@link RegisteredClient}. * * @author Anoop Garlapati */ public class RegisteredClientTests { private static final String ID = "registration-1"; private static final String CLIENT_ID = "client-1"; private static final String CLIENT_SECRET = "secret"; private static final Set REDIRECT_URIS = Collections.singleton("https://example.com"); private static final Set SCOPES = Collections.unmodifiableSet( Stream.of("openid", "profile", "email").collect(Collectors.toSet())); private static final Set CLIENT_AUTHENTICATION_METHODS = Collections.singleton(ClientAuthenticationMethod.BASIC); @Test public void buildWhenAuthorizationGrantTypesNotSetThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenAllAttributesProvidedThenAllAttributesAreSet() { RegisteredClient registration = RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getId()).isEqualTo(ID); assertThat(registration.getClientId()).isEqualTo(CLIENT_ID); assertThat(registration.getClientSecret()).isEqualTo(CLIENT_SECRET); assertThat(registration.getAuthorizationGrantTypes()) .isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE)); assertThat(registration.getClientAuthenticationMethods()).isEqualTo(CLIENT_AUTHENTICATION_METHODS); assertThat(registration.getRedirectUris()).isEqualTo(REDIRECT_URIS); assertThat(registration.getScopes()).isEqualTo(SCOPES); } @Test public void buildWhenIdIsNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(null)) .isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenClientIdIsNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(null) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUrisNotProvidedThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .scopes(scopes -> scopes.addAll(SCOPES)) .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUrisConsumerClearsSetThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUri("https://example.com") .redirectUris(Set::clear) .scopes(scopes -> scopes.addAll(SCOPES)) .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenClientAuthenticationMethodNotProvidedThenDefaultToBasic() { RegisteredClient registration = RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getClientAuthenticationMethods()) .isEqualTo(Collections.singleton(ClientAuthenticationMethod.BASIC)); } @Test public void buildWhenScopeIsEmptyThenScopeNotRequired() { RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .build(); } @Test public void buildWhenScopeConsumerIsProvidedThenConsumerAccepted() { RegisteredClient registration = RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getScopes()).isEqualTo(SCOPES); } @Test public void buildWhenScopeContainsSpaceThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(CLIENT_ID) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scope("openid profile") .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenScopeContainsInvalidCharacterThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scope("an\"invalid\"scope") .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUriInvalidThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUri("invalid URI") .scopes(scopes -> scopes.addAll(SCOPES)) .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUriContainsFragmentThenThrowIllegalArgumentException() { assertThatThrownBy(() -> RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUri("https://example.com/page#fragment") .scopes(scopes -> scopes.addAll(SCOPES)) .build() ).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenTwoAuthorizationGrantTypesAreProvidedThenBothAreRegistered() { RegisteredClient registration = RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getAuthorizationGrantTypes()) .containsExactlyInAnyOrder(AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); } @Test public void buildWhenAuthorizationGrantTypesConsumerIsProvidedThenConsumerAccepted() { RegisteredClient registration = RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantTypes(authorizationGrantTypes -> { authorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE); authorizationGrantTypes.add(AuthorizationGrantType.CLIENT_CREDENTIALS); }) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getAuthorizationGrantTypes()) .containsExactlyInAnyOrder(AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); } @Test public void buildWhenAuthorizationGrantTypesConsumerClearsSetThenThrowIllegalArgumentException() { assertThatThrownBy(() -> { RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantTypes(Set::clear) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); }).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenTwoClientAuthenticationMethodsAreProvidedThenBothAreRegistered() { RegisteredClient registration = RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .clientAuthenticationMethod(ClientAuthenticationMethod.POST) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getClientAuthenticationMethods()) .containsExactlyInAnyOrder(ClientAuthenticationMethod.BASIC, ClientAuthenticationMethod.POST); } @Test public void buildWhenClientAuthenticationMethodsConsumerIsProvidedThenConsumerAccepted() { RegisteredClient registration = RegisteredClient.withId(ID) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethods(clientAuthenticationMethods -> { clientAuthenticationMethods.add(ClientAuthenticationMethod.BASIC); clientAuthenticationMethods.add(ClientAuthenticationMethod.POST); }) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getClientAuthenticationMethods()) .containsExactlyInAnyOrder(ClientAuthenticationMethod.BASIC, ClientAuthenticationMethod.POST); } @Test public void buildWhenOverrideIdThenOverridden() { String overriddenId = "override"; RegisteredClient registration = RegisteredClient.withId(ID) .id(overriddenId) .clientId(CLIENT_ID) .clientSecret(CLIENT_SECRET) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC) .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) .scopes(scopes -> scopes.addAll(SCOPES)) .build(); assertThat(registration.getId()).isEqualTo(overriddenId); } @Test public void buildWhenRegisteredClientProvidedThenMakesACopy() { RegisteredClient registration = TestRegisteredClients.registeredClient().build(); RegisteredClient updated = RegisteredClient.from(registration).build(); assertThat(registration.getId()).isEqualTo(updated.getId()); assertThat(registration.getClientId()).isEqualTo(updated.getClientId()); assertThat(registration.getClientSecret()).isEqualTo(updated.getClientSecret()); assertThat(registration.getClientAuthenticationMethods()).isEqualTo(updated.getClientAuthenticationMethods()); assertThat(registration.getClientAuthenticationMethods()).isNotSameAs(updated.getClientAuthenticationMethods()); assertThat(registration.getAuthorizationGrantTypes()).isEqualTo(updated.getAuthorizationGrantTypes()); assertThat(registration.getAuthorizationGrantTypes()).isNotSameAs(updated.getAuthorizationGrantTypes()); assertThat(registration.getRedirectUris()).isEqualTo(updated.getRedirectUris()); assertThat(registration.getRedirectUris()).isNotSameAs(updated.getRedirectUris()); assertThat(registration.getScopes()).isEqualTo(updated.getScopes()); assertThat(registration.getScopes()).isNotSameAs(updated.getScopes()); assertThat(registration.getClientSettings().settings()).isEqualTo(updated.getClientSettings().settings()); assertThat(registration.getClientSettings()).isNotSameAs(updated.getClientSettings()); assertThat(registration.getTokenSettings().settings()).isEqualTo(updated.getTokenSettings().settings()); assertThat(registration.getTokenSettings()).isNotSameAs(updated.getTokenSettings()); } @Test public void buildWhenRegisteredClientValuesOverriddenThenPropagated() { RegisteredClient registration = TestRegisteredClients.registeredClient().build(); String newSecret = "new-secret"; String newScope = "new-scope"; String newRedirectUri = "https://another-redirect-uri.com"; RegisteredClient updated = RegisteredClient.from(registration) .clientSecret(newSecret) .scopes(scopes -> { scopes.clear(); scopes.add(newScope); }) .redirectUris(redirectUris -> { redirectUris.clear(); redirectUris.add(newRedirectUri); }) .build(); assertThat(registration.getClientSecret()).isNotEqualTo(newSecret); assertThat(updated.getClientSecret()).isEqualTo(newSecret); assertThat(registration.getScopes()).doesNotContain(newScope); assertThat(updated.getScopes()).containsExactly(newScope); assertThat(registration.getRedirectUris()).doesNotContain(newRedirectUri); assertThat(updated.getRedirectUris()).containsExactly(newRedirectUri); } }