18 changed files with 484 additions and 568 deletions
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* Copyright 2002-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.client.endpoint; |
||||
|
||||
import org.springframework.http.RequestEntity; |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
||||
import org.springframework.util.CollectionUtils; |
||||
import org.springframework.util.LinkedMultiValueMap; |
||||
import org.springframework.util.MultiValueMap; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* An implementation of an {@link AbstractOAuth2AuthorizationGrantRequestEntityConverter} |
||||
* that converts the provided {@link JwtBearerGrantRequest} to a {@link RequestEntity} |
||||
* representation of an OAuth 2.0 Access Token Request for the JWT Bearer Grant. |
||||
* |
||||
* @author Joe Grandja |
||||
* @since 5.5 |
||||
* @see AbstractOAuth2AuthorizationGrantRequestEntityConverter |
||||
* @see JwtBearerGrantRequest |
||||
* @see RequestEntity |
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7523#section-2.1">Section |
||||
* 2.1 Using JWTs as Authorization Grants</a> |
||||
*/ |
||||
public class JwtBearerGrantRequestEntityConverter |
||||
extends AbstractOAuth2AuthorizationGrantRequestEntityConverter<JwtBearerGrantRequest> { |
||||
|
||||
@Override |
||||
protected MultiValueMap<String, String> createParameters(JwtBearerGrantRequest jwtBearerGrantRequest) { |
||||
ClientRegistration clientRegistration = jwtBearerGrantRequest.getClientRegistration(); |
||||
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); |
||||
parameters.add(OAuth2ParameterNames.GRANT_TYPE, jwtBearerGrantRequest.getGrantType().getValue()); |
||||
parameters.add(OAuth2ParameterNames.ASSERTION, jwtBearerGrantRequest.getJwt().getTokenValue()); |
||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) { |
||||
parameters.add(OAuth2ParameterNames.SCOPE, |
||||
StringUtils.collectionToDelimitedString(clientRegistration.getScopes(), " ")); |
||||
} |
||||
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod()) |
||||
|| ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) { |
||||
parameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); |
||||
parameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); |
||||
} |
||||
return parameters; |
||||
} |
||||
|
||||
} |
||||
@ -1,92 +0,0 @@
@@ -1,92 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-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.client.endpoint; |
||||
|
||||
import java.net.URI; |
||||
|
||||
import org.springframework.core.convert.converter.Converter; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.RequestEntity; |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
||||
import org.springframework.util.CollectionUtils; |
||||
import org.springframework.util.LinkedMultiValueMap; |
||||
import org.springframework.util.MultiValueMap; |
||||
import org.springframework.util.StringUtils; |
||||
import org.springframework.web.util.UriComponentsBuilder; |
||||
|
||||
/** |
||||
* A {@link Converter} that converts the provided {@link OAuth2JwtBearerGrantRequest} to a |
||||
* {@link RequestEntity} representation of an OAuth 2.0 Access Token Request for the Jwt |
||||
* Bearer Grant. |
||||
* |
||||
* @author Joe Grandja |
||||
* @since 5.5 |
||||
* @see Converter |
||||
* @see OAuth2JwtBearerGrantRequest |
||||
* @see RequestEntity |
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7523#section-2.1">Section |
||||
* 2.1 JWTs as Authorization Grants</a> |
||||
*/ |
||||
public class OAuth2JwtBearerGrantRequestEntityConverter |
||||
implements Converter<OAuth2JwtBearerGrantRequest, RequestEntity<?>> { |
||||
|
||||
/** |
||||
* Returns the {@link RequestEntity} used for the Access Token Request. |
||||
* @param jwtBearerGrantRequest the Jwt Bearer grant request |
||||
* @return the {@link RequestEntity} used for the Access Token Request |
||||
*/ |
||||
@Override |
||||
public RequestEntity<?> convert(OAuth2JwtBearerGrantRequest jwtBearerGrantRequest) { |
||||
ClientRegistration clientRegistration = jwtBearerGrantRequest.getClientRegistration(); |
||||
|
||||
HttpHeaders headers = OAuth2AuthorizationGrantRequestEntityUtils.getTokenRequestHeaders(clientRegistration); |
||||
MultiValueMap<String, String> formParameters = this.buildFormParameters(jwtBearerGrantRequest); |
||||
URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri()).build() |
||||
.toUri(); |
||||
|
||||
return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri); |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link MultiValueMap} of the form parameters used for the Access Token |
||||
* Request body. |
||||
* @param jwtBearerGrantRequest the Jwt Bearer grant request |
||||
* @return a {@link MultiValueMap} of the form parameters used for the Access Token |
||||
* Request body |
||||
*/ |
||||
private MultiValueMap<String, String> buildFormParameters(OAuth2JwtBearerGrantRequest jwtBearerGrantRequest) { |
||||
ClientRegistration clientRegistration = jwtBearerGrantRequest.getClientRegistration(); |
||||
|
||||
MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>(); |
||||
formParameters.add(OAuth2ParameterNames.GRANT_TYPE, jwtBearerGrantRequest.getGrantType().getValue()); |
||||
formParameters.add(OAuth2ParameterNames.ASSERTION, jwtBearerGrantRequest.getJwt().getTokenValue()); |
||||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes())) { |
||||
formParameters.add(OAuth2ParameterNames.SCOPE, StringUtils |
||||
.collectionToDelimitedString(jwtBearerGrantRequest.getClientRegistration().getScopes(), " ")); |
||||
} |
||||
if (ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) { |
||||
formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId()); |
||||
formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret()); |
||||
} |
||||
|
||||
return formParameters; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
|
||||
/* |
||||
* Copyright 2002-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.client.endpoint; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.mockito.InOrder; |
||||
|
||||
import org.springframework.core.convert.converter.Converter; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.RequestEntity; |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations; |
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
||||
import org.springframework.security.oauth2.jwt.Jwt; |
||||
import org.springframework.security.oauth2.jwt.TestJwts; |
||||
import org.springframework.util.MultiValueMap; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.Mockito.inOrder; |
||||
import static org.mockito.Mockito.mock; |
||||
|
||||
/** |
||||
* Tests for {@link JwtBearerGrantRequestEntityConverter}. |
||||
* |
||||
* @author Hassene Laaribi |
||||
* @author Joe Grandja |
||||
*/ |
||||
public class JwtBearerGrantRequestEntityConverterTests { |
||||
|
||||
private JwtBearerGrantRequestEntityConverter converter; |
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.converter = new JwtBearerGrantRequestEntityConverter(); |
||||
} |
||||
|
||||
@Test |
||||
public void setHeadersConverterWhenNullThenThrowIllegalArgumentException() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setHeadersConverter(null)) |
||||
.withMessage("headersConverter cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void addHeadersConverterWhenNullThenThrowIllegalArgumentException() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addHeadersConverter(null)) |
||||
.withMessage("headersConverter cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void setParametersConverterWhenNullThenThrowIllegalArgumentException() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.converter.setParametersConverter(null)) |
||||
.withMessage("parametersConverter cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void addParametersConverterWhenNullThenThrowIllegalArgumentException() { |
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.converter.addParametersConverter(null)) |
||||
.withMessage("parametersConverter cannot be null"); |
||||
} |
||||
|
||||
@Test |
||||
public void convertWhenHeadersConverterSetThenCalled() { |
||||
Converter<JwtBearerGrantRequest, HttpHeaders> headersConverter1 = mock(Converter.class); |
||||
this.converter.setHeadersConverter(headersConverter1); |
||||
Converter<JwtBearerGrantRequest, HttpHeaders> headersConverter2 = mock(Converter.class); |
||||
this.converter.addHeadersConverter(headersConverter2); |
||||
// @formatter:off
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration() |
||||
.authorizationGrantType(AuthorizationGrantType.JWT_BEARER) |
||||
.scope("read", "write") |
||||
.build(); |
||||
// @formatter:on
|
||||
Jwt jwtAssertion = TestJwts.jwt().build(); |
||||
JwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest(clientRegistration, jwtAssertion); |
||||
this.converter.convert(jwtBearerGrantRequest); |
||||
InOrder inOrder = inOrder(headersConverter1, headersConverter2); |
||||
inOrder.verify(headersConverter1).convert(any(JwtBearerGrantRequest.class)); |
||||
inOrder.verify(headersConverter2).convert(any(JwtBearerGrantRequest.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void convertWhenParametersConverterSetThenCalled() { |
||||
Converter<JwtBearerGrantRequest, MultiValueMap<String, String>> parametersConverter1 = mock(Converter.class); |
||||
this.converter.setParametersConverter(parametersConverter1); |
||||
Converter<JwtBearerGrantRequest, MultiValueMap<String, String>> parametersConverter2 = mock(Converter.class); |
||||
this.converter.addParametersConverter(parametersConverter2); |
||||
// @formatter:off
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration() |
||||
.authorizationGrantType(AuthorizationGrantType.JWT_BEARER) |
||||
.scope("read", "write") |
||||
.build(); |
||||
// @formatter:on
|
||||
Jwt jwtAssertion = TestJwts.jwt().build(); |
||||
JwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest(clientRegistration, jwtAssertion); |
||||
this.converter.convert(jwtBearerGrantRequest); |
||||
InOrder inOrder = inOrder(parametersConverter1, parametersConverter2); |
||||
inOrder.verify(parametersConverter1).convert(any(JwtBearerGrantRequest.class)); |
||||
inOrder.verify(parametersConverter2).convert(any(JwtBearerGrantRequest.class)); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Test |
||||
public void convertWhenGrantRequestValidThenConverts() { |
||||
// @formatter:off
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration() |
||||
.authorizationGrantType(AuthorizationGrantType.JWT_BEARER) |
||||
.scope("read", "write") |
||||
.build(); |
||||
// @formatter:on
|
||||
Jwt jwtAssertion = TestJwts.jwt().build(); |
||||
JwtBearerGrantRequest jwtBearerGrantRequest = new JwtBearerGrantRequest(clientRegistration, jwtAssertion); |
||||
RequestEntity<?> requestEntity = this.converter.convert(jwtBearerGrantRequest); |
||||
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST); |
||||
assertThat(requestEntity.getUrl().toASCIIString()) |
||||
.isEqualTo(clientRegistration.getProviderDetails().getTokenUri()); |
||||
HttpHeaders headers = requestEntity.getHeaders(); |
||||
assertThat(headers.getAccept()).contains(MediaType.valueOf(MediaType.APPLICATION_JSON_UTF8_VALUE)); |
||||
assertThat(headers.getContentType()) |
||||
.isEqualTo(MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8")); |
||||
assertThat(headers.getFirst(HttpHeaders.AUTHORIZATION)).startsWith("Basic "); |
||||
MultiValueMap<String, String> formParameters = (MultiValueMap<String, String>) requestEntity.getBody(); |
||||
assertThat(formParameters.getFirst(OAuth2ParameterNames.GRANT_TYPE)) |
||||
.isEqualTo(AuthorizationGrantType.JWT_BEARER.getValue()); |
||||
assertThat(formParameters.getFirst(OAuth2ParameterNames.ASSERTION)).isEqualTo(jwtAssertion.getTokenValue()); |
||||
assertThat(formParameters.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo("read write"); |
||||
} |
||||
|
||||
} |
||||
@ -1,82 +0,0 @@
@@ -1,82 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-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.client.endpoint; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.http.RequestEntity; |
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations; |
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
||||
import org.springframework.security.oauth2.jwt.Jwt; |
||||
import org.springframework.security.oauth2.jwt.TestJwts; |
||||
import org.springframework.util.MultiValueMap; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link OAuth2JwtBearerGrantRequestEntityConverter}. |
||||
* |
||||
* @author Hassene Laaribi |
||||
*/ |
||||
public class OAuth2JwtBearerGrantRequestEntityConverterTests { |
||||
|
||||
private static final String UTF_8_CHARSET = ";charset=UTF-8"; |
||||
|
||||
private final OAuth2JwtBearerGrantRequestEntityConverter converter = new OAuth2JwtBearerGrantRequestEntityConverter(); |
||||
|
||||
private OAuth2JwtBearerGrantRequest jwtBearerGrantRequest; |
||||
|
||||
private final Jwt jwtBearerToken = TestJwts.jwt().build(); |
||||
|
||||
@Before |
||||
public void setup() { |
||||
// @formatter:off
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration() |
||||
.authorizationGrantType(AuthorizationGrantType.JWT_BEARER) |
||||
.scope("read", "write") |
||||
.build(); |
||||
// @formatter:on
|
||||
this.jwtBearerGrantRequest = new OAuth2JwtBearerGrantRequest(clientRegistration, this.jwtBearerToken); |
||||
} |
||||
|
||||
@Test |
||||
public void convertWhenGrantRequestValidThenConverts() { |
||||
RequestEntity<?> requestEntity = this.converter.convert(this.jwtBearerGrantRequest); |
||||
ClientRegistration clientRegistration = this.jwtBearerGrantRequest.getClientRegistration(); |
||||
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.POST); |
||||
assertThat(requestEntity.getUrl().toASCIIString()) |
||||
.isEqualTo(clientRegistration.getProviderDetails().getTokenUri()); |
||||
HttpHeaders headers = requestEntity.getHeaders(); |
||||
assertThat(headers.getAccept()).contains(MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE + UTF_8_CHARSET)); |
||||
assertThat(headers.getContentType()) |
||||
.isEqualTo(MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED_VALUE + UTF_8_CHARSET)); |
||||
assertThat(headers.getFirst(HttpHeaders.AUTHORIZATION)).startsWith("Basic "); |
||||
MultiValueMap<String, String> formParameters = (MultiValueMap<String, String>) requestEntity.getBody(); |
||||
assertThat(formParameters.getFirst(OAuth2ParameterNames.GRANT_TYPE)) |
||||
.isEqualTo(AuthorizationGrantType.JWT_BEARER.getValue()); |
||||
assertThat(formParameters.getFirst(OAuth2ParameterNames.ASSERTION)) |
||||
.isEqualTo(this.jwtBearerToken.getTokenValue()); |
||||
assertThat(formParameters.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo("read write"); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue