12 changed files with 743 additions and 5 deletions
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
/* |
||||
* Copyright 2020 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.authentication; |
||||
|
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.Base64; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.core.AuthenticationException; |
||||
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; |
||||
import org.springframework.security.crypto.keygen.StringKeyGenerator; |
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken; |
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
||||
import org.springframework.security.oauth2.core.OAuth2Error; |
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes; |
||||
|
||||
/** |
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Client Credentials Grant. |
||||
* |
||||
* @author Alexey Nesterov |
||||
* @since 0.0.1 |
||||
* @see OAuth2ClientCredentialsAuthenticationToken |
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.4">Section 4.4 Client Credentials Grant</a> |
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.4.2">Section 4.4.2 Access Token Request</a> |
||||
*/ |
||||
|
||||
public class OAuth2ClientCredentialsAuthenticationProvider implements AuthenticationProvider { |
||||
|
||||
private final StringKeyGenerator accessTokenGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder()); |
||||
|
||||
@Override |
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { |
||||
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthenticationToken = |
||||
(OAuth2ClientCredentialsAuthenticationToken) authentication; |
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = null; |
||||
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(clientCredentialsAuthenticationToken.getPrincipal().getClass())) { |
||||
clientPrincipal = (OAuth2ClientAuthenticationToken) clientCredentialsAuthenticationToken.getPrincipal(); |
||||
} |
||||
|
||||
if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) { |
||||
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT)); |
||||
} |
||||
|
||||
Set<String> clientScopes = clientPrincipal.getRegisteredClient().getScopes(); |
||||
Set<String> requestedScopes = clientCredentialsAuthenticationToken.getScopes(); |
||||
if (!clientScopes.containsAll(requestedScopes)) { |
||||
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE)); |
||||
} |
||||
|
||||
if (requestedScopes == null || requestedScopes.isEmpty()) { |
||||
requestedScopes = clientScopes; |
||||
} |
||||
|
||||
String tokenValue = this.accessTokenGenerator.generateKey(); |
||||
Instant issuedAt = Instant.now(); |
||||
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); // TODO Allow configuration for access token lifespan
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, |
||||
tokenValue, issuedAt, expiresAt, requestedScopes); |
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken( |
||||
clientPrincipal.getRegisteredClient(), clientPrincipal, accessToken); |
||||
} |
||||
|
||||
@Override |
||||
public boolean supports(Class<?> authentication) { |
||||
return OAuth2ClientCredentialsAuthenticationToken.class.isAssignableFrom(authentication); |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
/* |
||||
* Copyright 2020 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.authentication; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.server.authorization.Version; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials Grant. |
||||
* |
||||
* @author Alexey Nesterov |
||||
* @since 0.0.1 |
||||
* @see Authentication |
||||
* @see OAuth2ClientCredentialsAuthenticationProvider |
||||
*/ |
||||
public class OAuth2ClientCredentialsAuthenticationToken extends AbstractAuthenticationToken { |
||||
|
||||
private static final long serialVersionUID = Version.SERIAL_VERSION_UID; |
||||
|
||||
private final Authentication clientPrincipal; |
||||
private final Set<String> scopes; |
||||
|
||||
public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, Set<String> scopes) { |
||||
super(Collections.emptyList()); |
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); |
||||
Assert.notNull(scopes, "scopes cannot be null"); |
||||
this.clientPrincipal = clientPrincipal; |
||||
this.scopes = scopes; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public OAuth2ClientCredentialsAuthenticationToken(OAuth2ClientAuthenticationToken clientPrincipal) { |
||||
this(clientPrincipal, Collections.EMPTY_SET); |
||||
} |
||||
|
||||
@Override |
||||
public Object getCredentials() { |
||||
return ""; |
||||
} |
||||
|
||||
@Override |
||||
public Object getPrincipal() { |
||||
return this.clientPrincipal; |
||||
} |
||||
|
||||
public Set<String> getScopes() { |
||||
return this.scopes; |
||||
} |
||||
} |
||||
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
/* |
||||
* Copyright 2020 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.web; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.core.convert.converter.Converter; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* A {@link Converter} that delegates actual conversion to one of the provided converters based on grant_type param of a request. |
||||
* Returns null is grant type is not specified or not supported. |
||||
* |
||||
* @author Alexey Nesterov |
||||
* @since 0.0.1 |
||||
*/ |
||||
public final class DelegatingAuthorizationGrantAuthenticationConverter implements Converter<HttpServletRequest, Authentication> { |
||||
|
||||
private final Map<AuthorizationGrantType, Converter<HttpServletRequest, Authentication>> converters; |
||||
|
||||
public DelegatingAuthorizationGrantAuthenticationConverter(Map<AuthorizationGrantType, Converter<HttpServletRequest, Authentication>> converters) { |
||||
Assert.notEmpty(converters, "converters cannot be empty"); |
||||
|
||||
this.converters = Collections.unmodifiableMap(converters); |
||||
} |
||||
|
||||
@Override |
||||
public Authentication convert(HttpServletRequest request) { |
||||
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE); |
||||
if (StringUtils.isEmpty(grantType)) { |
||||
return null; |
||||
} |
||||
|
||||
Converter<HttpServletRequest, Authentication> converter = this.converters.get(new AuthorizationGrantType(grantType)); |
||||
if (converter == null) { |
||||
return null; |
||||
} |
||||
|
||||
return converter.convert(request); |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
/* |
||||
* Copyright 2020 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.authentication; |
||||
|
||||
import java.util.Collections; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
|
||||
/** |
||||
* @author Alexey Nesterov |
||||
*/ |
||||
public class OAuth2ClientCredentialsAuthenticationProviderTests { |
||||
|
||||
private static final RegisteredClient EXISTING_CLIENT = TestRegisteredClients.registeredClient().build(); |
||||
private OAuth2ClientCredentialsAuthenticationProvider authenticationProvider; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
this.authenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider(); |
||||
} |
||||
|
||||
@Test |
||||
public void supportsWhenSupportedClassThenTrue() { |
||||
assertThat(this.authenticationProvider.supports(OAuth2ClientCredentialsAuthenticationToken.class)).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void supportsWhenUnsupportedClassThenFalse() { |
||||
assertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeAuthenticationProvider.class)).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
public void authenticateWhenValidAuthenticationThenReturnTokenWithClient() { |
||||
Authentication authentication = this.authenticationProvider.authenticate(getAuthentication()); |
||||
assertThat(authentication).isInstanceOf(OAuth2AccessTokenAuthenticationToken.class); |
||||
|
||||
OAuth2AccessTokenAuthenticationToken token = (OAuth2AccessTokenAuthenticationToken) authentication; |
||||
assertThat(token.getRegisteredClient()).isEqualTo(EXISTING_CLIENT); |
||||
} |
||||
|
||||
@Test |
||||
public void authenticateWhenValidAuthenticationThenGenerateTokenValue() { |
||||
Authentication authentication = this.authenticationProvider.authenticate(getAuthentication()); |
||||
OAuth2AccessTokenAuthenticationToken token = (OAuth2AccessTokenAuthenticationToken) authentication; |
||||
assertThat(token.getAccessToken().getTokenValue()).isNotBlank(); |
||||
} |
||||
|
||||
@Test |
||||
public void authenticateWhenValidateScopeThenReturnTokenWithScopes() { |
||||
Authentication authentication = this.authenticationProvider.authenticate(getAuthentication()); |
||||
OAuth2AccessTokenAuthenticationToken token = (OAuth2AccessTokenAuthenticationToken) authentication; |
||||
assertThat(token.getAccessToken().getScopes()).containsAll(EXISTING_CLIENT.getScopes()); |
||||
} |
||||
|
||||
@Test |
||||
public void authenticateWhenNoScopeRequestedThenUseDefaultScopes() { |
||||
OAuth2ClientCredentialsAuthenticationToken authenticationToken = new OAuth2ClientCredentialsAuthenticationToken(new OAuth2ClientAuthenticationToken(EXISTING_CLIENT)); |
||||
Authentication authentication = this.authenticationProvider.authenticate(authenticationToken); |
||||
OAuth2AccessTokenAuthenticationToken token = (OAuth2AccessTokenAuthenticationToken) authentication; |
||||
assertThat(token.getAccessToken().getScopes()).containsAll(EXISTING_CLIENT.getScopes()); |
||||
} |
||||
|
||||
@Test |
||||
public void authenticateWhenInvalidSecretThenThrowException() { |
||||
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( |
||||
new OAuth2ClientAuthenticationToken(EXISTING_CLIENT.getClientId(), "not-a-valid-secret")); |
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) |
||||
.isInstanceOf(OAuth2AuthenticationException.class); |
||||
} |
||||
|
||||
@Test |
||||
public void authenticateWhenNonExistingClientThenThrowException() { |
||||
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( |
||||
new OAuth2ClientAuthenticationToken("another-client-id", "another-secret")); |
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) |
||||
.isInstanceOf(OAuth2AuthenticationException.class); |
||||
} |
||||
|
||||
@Test |
||||
public void authenticateWhenInvalidScopesThenThrowException() { |
||||
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( |
||||
new OAuth2ClientAuthenticationToken(EXISTING_CLIENT), Collections.singleton("non-existing-scope")); |
||||
|
||||
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) |
||||
.isInstanceOf(OAuth2AuthenticationException.class); |
||||
} |
||||
|
||||
private OAuth2ClientCredentialsAuthenticationToken getAuthentication() { |
||||
return new OAuth2ClientCredentialsAuthenticationToken(new OAuth2ClientAuthenticationToken(EXISTING_CLIENT), EXISTING_CLIENT.getScopes()); |
||||
} |
||||
} |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
* Copyright 2020 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.authentication; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
|
||||
/** |
||||
* @author Alexey Nesterov |
||||
*/ |
||||
public class OAuth2ClientCredentialsAuthenticationTokenTests { |
||||
|
||||
private final OAuth2ClientAuthenticationToken clientPrincipal = |
||||
new OAuth2ClientAuthenticationToken(TestRegisteredClients.registeredClient().build()); |
||||
|
||||
@Test |
||||
public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { |
||||
assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(null)) |
||||
.isInstanceOf(IllegalArgumentException.class) |
||||
.hasMessage("clientPrincipal cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorWhenScopesNullThenThrowIllegalArgumentException() { |
||||
assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null)) |
||||
.isInstanceOf(IllegalArgumentException.class) |
||||
.hasMessage("scopes cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorWhenClientPrincipalProvidedThenCreated() { |
||||
OAuth2ClientCredentialsAuthenticationToken authentication |
||||
= new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal); |
||||
|
||||
assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal); |
||||
assertThat(authentication.getCredentials().toString()).isEmpty(); |
||||
assertThat(authentication.getScopes()).isEmpty(); |
||||
} |
||||
|
||||
@Test |
||||
public void constructorWhenScopesProvidedThenCreated() { |
||||
Set<String> expectedScopes = Collections.singleton("test-scope"); |
||||
|
||||
OAuth2ClientCredentialsAuthenticationToken authentication |
||||
= new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, expectedScopes); |
||||
|
||||
assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal); |
||||
assertThat(authentication.getCredentials().toString()).isEmpty(); |
||||
assertThat(authentication.getScopes()).containsAll(expectedScopes); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
|
||||
/* |
||||
* Copyright 2020 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.web; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
import java.util.Collections; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.core.convert.converter.Converter; |
||||
import org.springframework.mock.web.MockHttpServletRequest; |
||||
import org.springframework.mock.web.MockServletContext; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
||||
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; |
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
/** |
||||
* @author Alexey Nesterov |
||||
*/ |
||||
public class DelegatingAuthorizationGrantAuthenticationConverterTests { |
||||
|
||||
private DelegatingAuthorizationGrantAuthenticationConverter authenticationConverter; |
||||
private Converter<HttpServletRequest, Authentication> clientCredentialsConverterMock; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
clientCredentialsConverterMock = mock(Converter.class); |
||||
Map<AuthorizationGrantType, Converter<HttpServletRequest, Authentication>> converters |
||||
= Collections.singletonMap(AuthorizationGrantType.CLIENT_CREDENTIALS, clientCredentialsConverterMock); |
||||
authenticationConverter = new DelegatingAuthorizationGrantAuthenticationConverter(converters); |
||||
} |
||||
|
||||
@Test |
||||
public void convertWhenAuthorizationGrantTypeSupportedThenConverterCalled() { |
||||
MockHttpServletRequest request = MockMvcRequestBuilders |
||||
.post("/oauth/token") |
||||
.param("grant_type", "client_credentials") |
||||
.buildRequest(new MockServletContext()); |
||||
|
||||
OAuth2ClientAuthenticationToken expectedAuthentication = new OAuth2ClientAuthenticationToken("id", "secret"); |
||||
when(clientCredentialsConverterMock.convert(request)).thenReturn(expectedAuthentication); |
||||
|
||||
Authentication actualAuthentication = authenticationConverter.convert(request); |
||||
|
||||
verify(clientCredentialsConverterMock).convert(request); |
||||
assertThat(actualAuthentication).isEqualTo(expectedAuthentication); |
||||
} |
||||
|
||||
@Test |
||||
public void convertWhenAuthorizationGrantTypeNotSupportedThenNull() { |
||||
MockHttpServletRequest request = MockMvcRequestBuilders |
||||
.post("/oauth/token") |
||||
.param("grant_type", "authorization_code") |
||||
.buildRequest(new MockServletContext()); |
||||
|
||||
Authentication actualAuthentication = authenticationConverter.convert(request); |
||||
|
||||
verifyNoInteractions(clientCredentialsConverterMock); |
||||
assertThat(actualAuthentication).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void convertWhenNoAuthorizationGrantTypeThenNull() { |
||||
MockHttpServletRequest request = MockMvcRequestBuilders |
||||
.post("/oauth/token") |
||||
.buildRequest(new MockServletContext()); |
||||
|
||||
Authentication actualAuthentication = authenticationConverter.convert(request); |
||||
|
||||
verifyNoInteractions(clientCredentialsConverterMock); |
||||
assertThat(actualAuthentication).isNull(); |
||||
} |
||||
} |
||||
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
/* |
||||
* Copyright 2020 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.web; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.Rule; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||
import org.springframework.security.config.annotation.web.configuration.oauth2.server.authorization.OAuth2AuthorizationServerConfiguration; |
||||
import org.springframework.security.config.test.SpringTestRule; |
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; |
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; |
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; |
||||
import org.springframework.test.web.servlet.MockMvc; |
||||
|
||||
import static org.hamcrest.CoreMatchers.endsWith; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.reset; |
||||
import static org.mockito.Mockito.when; |
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; |
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; |
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; |
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
||||
/** |
||||
* @author Alexey Nesterov |
||||
*/ |
||||
public class OAuth2ClientCredentialsGrantTests { |
||||
|
||||
private static RegisteredClientRepository registeredClientRepository; |
||||
private static OAuth2AuthorizationService authorizationService; |
||||
|
||||
@Rule |
||||
public final SpringTestRule spring = new SpringTestRule(); |
||||
|
||||
@Autowired |
||||
private MockMvc mvc; |
||||
|
||||
@BeforeClass |
||||
public static void init() { |
||||
registeredClientRepository = mock(RegisteredClientRepository.class); |
||||
authorizationService = mock(OAuth2AuthorizationService.class); |
||||
} |
||||
|
||||
@Before |
||||
public void setup() { |
||||
reset(registeredClientRepository); |
||||
reset(authorizationService); |
||||
} |
||||
|
||||
@Test |
||||
public void requestWhenTokenRequestAuthenticatedThenThenReturnTokenAndScope() throws Exception { |
||||
this.spring.register(AuthorizationServerConfiguration.class).autowire(); |
||||
RegisteredClient client = TestRegisteredClients.registeredClient().build(); |
||||
when(registeredClientRepository.findByClientId(client.getClientId())) |
||||
.thenReturn(client); |
||||
|
||||
this.mvc.perform(post(OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI) |
||||
.with(httpBasic(client.getClientId(), client.getClientSecret())) |
||||
.with(csrf()) |
||||
.param("grant_type", "client_credentials") |
||||
.param("scope", "email openid")) |
||||
.andExpect(status().isOk()) |
||||
.andExpect(jsonPath("$.access_token").isNotEmpty()) |
||||
.andExpect(jsonPath("$.scope").value("openid email")); |
||||
} |
||||
|
||||
@Test |
||||
public void requestWhenTokenRequestNotAuthenticatedThenRedirect() throws Exception { |
||||
this.spring.register(AuthorizationServerConfiguration.class).autowire(); |
||||
RegisteredClient client = TestRegisteredClients.registeredClient().build(); |
||||
when(registeredClientRepository.findByClientId(client.getClientId())) |
||||
.thenReturn(client); |
||||
|
||||
this.mvc.perform(post(OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI) |
||||
.with(csrf()) |
||||
.param("grant_type", "client_credentials") |
||||
.param("scope", "email openid")) |
||||
.andExpect(status().isFound()) |
||||
.andExpect(header().string("Location", endsWith("/login"))); |
||||
} |
||||
|
||||
@EnableWebSecurity |
||||
@Import(OAuth2AuthorizationServerConfiguration.class) |
||||
static class AuthorizationServerConfiguration { |
||||
|
||||
@Bean |
||||
RegisteredClientRepository registeredClientRepository() { |
||||
return registeredClientRepository; |
||||
} |
||||
|
||||
@Bean |
||||
OAuth2AuthorizationService authorizationService() { |
||||
return authorizationService; |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue