Add custom parameters to token introspection requests

Added support for providing custom parameters to an OAuth 2.0 token
introspection request. This is done by explicitly instantiating a
NimbusOAuth2TokenIntrospectionClient instance and then setting a custom
Converter implementation.

Fixes gh-6798
This commit is contained in:
MD Sayem Ahmed 2019-05-14 05:55:59 +02:00 committed by Josh Cummings
parent 69e4e3ed88
commit 0bc60dca69
2 changed files with 59 additions and 8 deletions

View File

@ -30,6 +30,7 @@ import com.nimbusds.oauth2.sdk.TokenIntrospectionSuccessResponse;
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import com.nimbusds.oauth2.sdk.id.Audience;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
@ -54,10 +55,11 @@ import static org.springframework.security.oauth2.server.resource.introspection.
* A Nimbus implementation of {@link OAuth2TokenIntrospectionClient}.
*
* @author Josh Cummings
* @author MD Sayem Ahmed
* @since 5.2
*/
public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospectionClient {
private URI introspectionUri;
private Converter<String, RequestEntity<?>> requestEntityConverter;
private RestOperations restOperations;
/**
@ -72,7 +74,7 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
Assert.notNull(clientId, "clientId cannot be null");
Assert.notNull(clientSecret, "clientSecret cannot be null");
this.introspectionUri = URI.create(introspectionUri);
this.requestEntityConverter = this.defaultRequestEntityConverter(introspectionUri);
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(clientId, clientSecret));
this.restOperations = restTemplate;
@ -91,7 +93,7 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
Assert.notNull(introspectionUri, "introspectionUri cannot be null");
Assert.notNull(restOperations, "restOperations cannot be null");
this.introspectionUri = URI.create(introspectionUri);
this.requestEntityConverter = this.defaultRequestEntityConverter(introspectionUri);
this.restOperations = restOperations;
}
@ -101,7 +103,7 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
@Override
public Map<String, Object> introspect(String token) {
TokenIntrospectionSuccessResponse response = Optional.of(token)
.map(this::buildRequest)
.map(this.requestEntityConverter::convert)
.map(this::makeRequest)
.map(this::adaptToNimbusResponse)
.map(this::parseNimbusResponse)
@ -112,10 +114,25 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
return convertClaimsSet(response);
}
private RequestEntity<MultiValueMap<String, String>> buildRequest(String token) {
HttpHeaders headers = requestHeaders();
MultiValueMap<String, String> body = requestBody(token);
return new RequestEntity<>(body, headers, HttpMethod.POST, this.introspectionUri);
/**
* Sets the {@link Converter} used for converting the OAuth 2.0 access token to a {@link RequestEntity}
* representation of the OAuth 2.0 token introspection request.
*
* @param requestEntityConverter the {@link Converter} used for converting to a {@link RequestEntity} representation
* of the token introspection request
*/
public void setRequestEntityConverter(Converter<String, RequestEntity<?>> requestEntityConverter) {
Assert.notNull(requestEntityConverter, "requestEntityConverter cannot be null");
this.requestEntityConverter = requestEntityConverter;
}
private Converter<String, RequestEntity<?>> defaultRequestEntityConverter(String introspectionUri) {
return token -> {
HttpHeaders headers = requestHeaders();
MultiValueMap<String, String> body = requestBody(token);
return new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(introspectionUri));
};
}
private HttpHeaders requestHeaders() {

View File

@ -32,6 +32,7 @@ import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.Test;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -45,9 +46,11 @@ import org.springframework.web.client.RestOperations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.AUDIENCE;
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
@ -254,6 +257,37 @@ public class NimbusOAuth2TokenIntrospectionClientTests {
.isInstanceOf(IllegalArgumentException.class);
}
@Test
public void setRequestEntityConverterWhenConverterIsNullThenExceptionIsThrown() {
RestOperations restOperations = mock(RestOperations.class);
NimbusOAuth2TokenIntrospectionClient introspectionClient = new NimbusOAuth2TokenIntrospectionClient(
INTROSPECTION_URL, restOperations
);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> introspectionClient.setRequestEntityConverter(null));
}
@SuppressWarnings("unchecked")
@Test
public void setRequestEntityConverterWhenNonNullConverterGivenThenConverterUsed() {
RestOperations restOperations = mock(RestOperations.class);
Converter<String, RequestEntity<?>> requestEntityConverter = mock(Converter.class);
RequestEntity requestEntity = mock(RequestEntity.class);
String tokenToIntrospect = "some token";
when(requestEntityConverter.convert(tokenToIntrospect)).thenReturn(requestEntity);
when(restOperations.exchange(requestEntity, String.class)).thenReturn(ACTIVE);
NimbusOAuth2TokenIntrospectionClient introspectionClient = new NimbusOAuth2TokenIntrospectionClient(
INTROSPECTION_URL, restOperations
);
introspectionClient.setRequestEntityConverter(requestEntityConverter);
introspectionClient.introspect(tokenToIntrospect);
verify(requestEntityConverter).convert(tokenToIntrospect);
}
private static ResponseEntity<String> response(String content) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);