2
0
Эх сурвалжийг харах

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
MD Sayem Ahmed 6 жил өмнө
parent
commit
0bc60dca69

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

@@ -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() {

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

@@ -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);