Skip to content

Commit 7e570ae

Browse files
committed
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 spring-projectsgh-6798
1 parent 69e4e3e commit 7e570ae

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOAuth2TokenIntrospectionClient.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
3131
import com.nimbusds.oauth2.sdk.id.Audience;
3232

33+
import org.springframework.core.convert.converter.Converter;
3334
import org.springframework.http.HttpHeaders;
3435
import org.springframework.http.HttpMethod;
3536
import org.springframework.http.MediaType;
@@ -54,10 +55,11 @@
5455
* A Nimbus implementation of {@link OAuth2TokenIntrospectionClient}.
5556
*
5657
* @author Josh Cummings
58+
* @author MD Sayem Ahmed
5759
* @since 5.2
5860
*/
5961
public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospectionClient {
60-
private URI introspectionUri;
62+
private Converter<String, RequestEntity<?>> requestEntityConverter;
6163
private RestOperations restOperations;
6264

6365
/**
@@ -72,7 +74,7 @@ public NimbusOAuth2TokenIntrospectionClient(String introspectionUri, String clie
7274
Assert.notNull(clientId, "clientId cannot be null");
7375
Assert.notNull(clientSecret, "clientSecret cannot be null");
7476

75-
this.introspectionUri = URI.create(introspectionUri);
77+
this.requestEntityConverter = this.defaultRequestEntityConverter(introspectionUri);
7678
RestTemplate restTemplate = new RestTemplate();
7779
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(clientId, clientSecret));
7880
this.restOperations = restTemplate;
@@ -91,7 +93,7 @@ public NimbusOAuth2TokenIntrospectionClient(String introspectionUri, RestOperati
9193
Assert.notNull(introspectionUri, "introspectionUri cannot be null");
9294
Assert.notNull(restOperations, "restOperations cannot be null");
9395

94-
this.introspectionUri = URI.create(introspectionUri);
96+
this.requestEntityConverter = this.defaultRequestEntityConverter(introspectionUri);
9597
this.restOperations = restOperations;
9698
}
9799

@@ -101,7 +103,7 @@ public NimbusOAuth2TokenIntrospectionClient(String introspectionUri, RestOperati
101103
@Override
102104
public Map<String, Object> introspect(String token) {
103105
TokenIntrospectionSuccessResponse response = Optional.of(token)
104-
.map(this::buildRequest)
106+
.map(this.requestEntityConverter::convert)
105107
.map(this::makeRequest)
106108
.map(this::adaptToNimbusResponse)
107109
.map(this::parseNimbusResponse)
@@ -112,10 +114,25 @@ public Map<String, Object> introspect(String token) {
112114
return convertClaimsSet(response);
113115
}
114116

115-
private RequestEntity<MultiValueMap<String, String>> buildRequest(String token) {
116-
HttpHeaders headers = requestHeaders();
117-
MultiValueMap<String, String> body = requestBody(token);
118-
return new RequestEntity<>(body, headers, HttpMethod.POST, this.introspectionUri);
117+
/**
118+
* Sets the {@link Converter} used for converting the OAuth 2.0 access token to a {@link RequestEntity}
119+
* representation of the OAuth 2.0 token introspection request.
120+
*
121+
* @param requestEntityConverter the {@link Converter} used for converting to a {@link RequestEntity} representation
122+
* of the token introspection request
123+
*/
124+
public void setRequestEntityConverter(Converter<String, RequestEntity<?>> requestEntityConverter) {
125+
Assert.notNull(requestEntityConverter, "requestEntityConverter cannot be null");
126+
127+
this.requestEntityConverter = requestEntityConverter;
128+
}
129+
130+
private Converter<String, RequestEntity<?>> defaultRequestEntityConverter(String introspectionUri) {
131+
return token -> {
132+
HttpHeaders headers = requestHeaders();
133+
MultiValueMap<String, String> body = requestBody(token);
134+
return new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(introspectionUri));
135+
};
119136
}
120137

121138
private HttpHeaders requestHeaders() {

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOAuth2TokenIntrospectionClientTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import okhttp3.mockwebserver.RecordedRequest;
3333
import org.junit.Test;
3434

35+
import org.springframework.core.convert.converter.Converter;
3536
import org.springframework.http.HttpHeaders;
3637
import org.springframework.http.HttpStatus;
3738
import org.springframework.http.MediaType;
@@ -45,9 +46,11 @@
4546

4647
import static org.assertj.core.api.Assertions.assertThat;
4748
import static org.assertj.core.api.Assertions.assertThatCode;
49+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
4850
import static org.mockito.ArgumentMatchers.any;
4951
import static org.mockito.ArgumentMatchers.eq;
5052
import static org.mockito.Mockito.mock;
53+
import static org.mockito.Mockito.verify;
5154
import static org.mockito.Mockito.when;
5255
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.AUDIENCE;
5356
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
@@ -254,6 +257,37 @@ public void constructorWhenRestOperationsIsNullThenIllegalArgumentException() {
254257
.isInstanceOf(IllegalArgumentException.class);
255258
}
256259

260+
@Test
261+
public void setRequestEntityConverterWhenConverterIsNullThenExceptionIsThrown() {
262+
RestOperations restOperations = mock(RestOperations.class);
263+
264+
NimbusOAuth2TokenIntrospectionClient introspectionClient = new NimbusOAuth2TokenIntrospectionClient(
265+
INTROSPECTION_URL, restOperations
266+
);
267+
268+
assertThatExceptionOfType(IllegalArgumentException.class)
269+
.isThrownBy(() -> introspectionClient.setRequestEntityConverter(null));
270+
}
271+
272+
@SuppressWarnings("unchecked")
273+
@Test
274+
public void setRequestEntityConverterWhenNonNullConverterGivenThenConverterUsed() {
275+
RestOperations restOperations = mock(RestOperations.class);
276+
Converter<String, RequestEntity<?>> requestEntityConverter = mock(Converter.class);
277+
RequestEntity requestEntity = mock(RequestEntity.class);
278+
String tokenToIntrospect = "some token";
279+
when(requestEntityConverter.convert(tokenToIntrospect)).thenReturn(requestEntity);
280+
when(restOperations.exchange(requestEntity, String.class)).thenReturn(ACTIVE);
281+
NimbusOAuth2TokenIntrospectionClient introspectionClient = new NimbusOAuth2TokenIntrospectionClient(
282+
INTROSPECTION_URL, restOperations
283+
);
284+
introspectionClient.setRequestEntityConverter(requestEntityConverter);
285+
286+
introspectionClient.introspect(tokenToIntrospect);
287+
288+
verify(requestEntityConverter).convert(tokenToIntrospect);
289+
}
290+
257291
private static ResponseEntity<String> response(String content) {
258292
HttpHeaders headers = new HttpHeaders();
259293
headers.setContentType(MediaType.APPLICATION_JSON);

0 commit comments

Comments
 (0)