Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand All @@ -54,10 +55,11 @@
* 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;

/**
Expand All @@ -72,7 +74,7 @@ public NimbusOAuth2TokenIntrospectionClient(String introspectionUri, String clie
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;
Expand All @@ -91,7 +93,7 @@ public NimbusOAuth2TokenIntrospectionClient(String introspectionUri, RestOperati
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;
}

Expand All @@ -101,7 +103,7 @@ public NimbusOAuth2TokenIntrospectionClient(String introspectionUri, RestOperati
@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)
Expand All @@ -112,10 +114,25 @@ public Map<String, Object> introspect(String token) {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand All @@ -45,9 +46,11 @@

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;
Expand Down Expand Up @@ -254,6 +257,37 @@ public void constructorWhenRestOperationsIsNullThenIllegalArgumentException() {
.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);
Expand Down