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:
parent
69e4e3ed88
commit
0bc60dca69
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue