mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 09:12:14 +00:00
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.http.HTTPResponse;
|
||||||
import com.nimbusds.oauth2.sdk.id.Audience;
|
import com.nimbusds.oauth2.sdk.id.Audience;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@ -54,10 +55,11 @@ import static org.springframework.security.oauth2.server.resource.introspection.
|
|||||||
* A Nimbus implementation of {@link OAuth2TokenIntrospectionClient}.
|
* A Nimbus implementation of {@link OAuth2TokenIntrospectionClient}.
|
||||||
*
|
*
|
||||||
* @author Josh Cummings
|
* @author Josh Cummings
|
||||||
|
* @author MD Sayem Ahmed
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospectionClient {
|
public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospectionClient {
|
||||||
private URI introspectionUri;
|
private Converter<String, RequestEntity<?>> requestEntityConverter;
|
||||||
private RestOperations restOperations;
|
private RestOperations restOperations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,7 +74,7 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
|
|||||||
Assert.notNull(clientId, "clientId cannot be null");
|
Assert.notNull(clientId, "clientId cannot be null");
|
||||||
Assert.notNull(clientSecret, "clientSecret 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 restTemplate = new RestTemplate();
|
||||||
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(clientId, clientSecret));
|
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(clientId, clientSecret));
|
||||||
this.restOperations = restTemplate;
|
this.restOperations = restTemplate;
|
||||||
@ -91,7 +93,7 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
|
|||||||
Assert.notNull(introspectionUri, "introspectionUri cannot be null");
|
Assert.notNull(introspectionUri, "introspectionUri cannot be null");
|
||||||
Assert.notNull(restOperations, "restOperations cannot be null");
|
Assert.notNull(restOperations, "restOperations cannot be null");
|
||||||
|
|
||||||
this.introspectionUri = URI.create(introspectionUri);
|
this.requestEntityConverter = this.defaultRequestEntityConverter(introspectionUri);
|
||||||
this.restOperations = restOperations;
|
this.restOperations = restOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +103,7 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
|
|||||||
@Override
|
@Override
|
||||||
public Map<String, Object> introspect(String token) {
|
public Map<String, Object> introspect(String token) {
|
||||||
TokenIntrospectionSuccessResponse response = Optional.of(token)
|
TokenIntrospectionSuccessResponse response = Optional.of(token)
|
||||||
.map(this::buildRequest)
|
.map(this.requestEntityConverter::convert)
|
||||||
.map(this::makeRequest)
|
.map(this::makeRequest)
|
||||||
.map(this::adaptToNimbusResponse)
|
.map(this::adaptToNimbusResponse)
|
||||||
.map(this::parseNimbusResponse)
|
.map(this::parseNimbusResponse)
|
||||||
@ -112,10 +114,25 @@ public class NimbusOAuth2TokenIntrospectionClient implements OAuth2TokenIntrospe
|
|||||||
return convertClaimsSet(response);
|
return convertClaimsSet(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<MultiValueMap<String, String>> buildRequest(String token) {
|
/**
|
||||||
HttpHeaders headers = requestHeaders();
|
* Sets the {@link Converter} used for converting the OAuth 2.0 access token to a {@link RequestEntity}
|
||||||
MultiValueMap<String, String> body = requestBody(token);
|
* representation of the OAuth 2.0 token introspection request.
|
||||||
return new RequestEntity<>(body, headers, HttpMethod.POST, this.introspectionUri);
|
*
|
||||||
|
* @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() {
|
private HttpHeaders requestHeaders() {
|
||||||
|
@ -32,6 +32,7 @@ import okhttp3.mockwebserver.MockWebServer;
|
|||||||
import okhttp3.mockwebserver.RecordedRequest;
|
import okhttp3.mockwebserver.RecordedRequest;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
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.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
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.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
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.AUDIENCE;
|
||||||
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
|
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
|
||||||
@ -254,6 +257,37 @@ public class NimbusOAuth2TokenIntrospectionClientTests {
|
|||||||
.isInstanceOf(IllegalArgumentException.class);
|
.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) {
|
private static ResponseEntity<String> response(String content) {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user