parent
d5ae4337e3
commit
36ae1fe3f9
|
@ -87,23 +87,38 @@ public final class BearerTokenError extends OAuth2Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isDescriptionValid(String description) {
|
private static boolean isDescriptionValid(String description) {
|
||||||
return description == null || description.chars().allMatch((c) -> withinTheRangeOf(c, 0x20, 0x21)
|
// @formatter:off
|
||||||
|| withinTheRangeOf(c, 0x23, 0x5B) || withinTheRangeOf(c, 0x5D, 0x7E));
|
return description == null || description.chars().allMatch((c) ->
|
||||||
|
withinTheRangeOf(c, 0x20, 0x21) ||
|
||||||
|
withinTheRangeOf(c, 0x23, 0x5B) ||
|
||||||
|
withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isErrorCodeValid(String errorCode) {
|
private static boolean isErrorCodeValid(String errorCode) {
|
||||||
return errorCode.chars().allMatch((c) -> withinTheRangeOf(c, 0x20, 0x21) || withinTheRangeOf(c, 0x23, 0x5B)
|
// @formatter:off
|
||||||
|| withinTheRangeOf(c, 0x5D, 0x7E));
|
return errorCode.chars().allMatch((c) ->
|
||||||
|
withinTheRangeOf(c, 0x20, 0x21) ||
|
||||||
|
withinTheRangeOf(c, 0x23, 0x5B) ||
|
||||||
|
withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isErrorUriValid(String errorUri) {
|
private static boolean isErrorUriValid(String errorUri) {
|
||||||
return errorUri == null || errorUri.chars()
|
return errorUri == null || errorUri.chars()
|
||||||
.allMatch((c) -> c == 0x21 || withinTheRangeOf(c, 0x23, 0x5B) || withinTheRangeOf(c, 0x5D, 0x7E));
|
.allMatch((c) ->
|
||||||
|
c == 0x21 ||
|
||||||
|
withinTheRangeOf(c, 0x23, 0x5B) ||
|
||||||
|
withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isScopeValid(String scope) {
|
private static boolean isScopeValid(String scope) {
|
||||||
return scope == null || scope.chars().allMatch((c) -> withinTheRangeOf(c, 0x20, 0x21)
|
// @formatter:off
|
||||||
|| withinTheRangeOf(c, 0x23, 0x5B) || withinTheRangeOf(c, 0x5D, 0x7E));
|
return scope == null || scope.chars().allMatch((c) ->
|
||||||
|
withinTheRangeOf(c, 0x20, 0x21) ||
|
||||||
|
withinTheRangeOf(c, 0x23, 0x5B) ||
|
||||||
|
withinTheRangeOf(c, 0x5D, 0x7E));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean withinTheRangeOf(int c, int min, int max) {
|
private static boolean withinTheRangeOf(int c, int min, int max) {
|
||||||
|
|
|
@ -122,9 +122,13 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Mono<ReactiveAuthenticationManager> resolve(ServerWebExchange exchange) {
|
public Mono<ReactiveAuthenticationManager> resolve(ServerWebExchange exchange) {
|
||||||
|
// @formatter:off
|
||||||
return this.issuerConverter.convert(exchange)
|
return this.issuerConverter.convert(exchange)
|
||||||
.flatMap((issuer) -> this.issuerAuthenticationManagerResolver.resolve(issuer)
|
.flatMap((issuer) -> this.issuerAuthenticationManagerResolver
|
||||||
.switchIfEmpty(Mono.error(() -> new InvalidBearerTokenException("Invalid issuer " + issuer))));
|
.resolve(issuer)
|
||||||
|
.switchIfEmpty(Mono.error(() -> new InvalidBearerTokenException("Invalid issuer " + issuer)))
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class JwtClaimIssuerConverter implements Converter<ServerWebExchange, Mono<String>> {
|
private static class JwtClaimIssuerConverter implements Converter<ServerWebExchange, Mono<String>> {
|
||||||
|
@ -166,10 +170,13 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
|
||||||
if (!this.trustedIssuer.test(issuer)) {
|
if (!this.trustedIssuer.test(issuer)) {
|
||||||
return Mono.empty();
|
return Mono.empty();
|
||||||
}
|
}
|
||||||
|
// @formatter:off
|
||||||
return this.authenticationManagers.computeIfAbsent(issuer,
|
return this.authenticationManagers.computeIfAbsent(issuer,
|
||||||
(k) -> Mono.<ReactiveAuthenticationManager>fromCallable(
|
(k) -> Mono.<ReactiveAuthenticationManager>fromCallable(() -> new JwtReactiveAuthenticationManager(ReactiveJwtDecoders.fromIssuerLocation(k)))
|
||||||
() -> new JwtReactiveAuthenticationManager(ReactiveJwtDecoders.fromIssuerLocation(k)))
|
.subscribeOn(Schedulers.boundedElastic())
|
||||||
.subscribeOn(Schedulers.boundedElastic()).cache());
|
.cache()
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,16 @@ public final class JwtReactiveAuthenticationManager implements ReactiveAuthentic
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||||
return Mono.justOrEmpty(authentication).filter((a) -> a instanceof BearerTokenAuthenticationToken)
|
// @formatter:off
|
||||||
.cast(BearerTokenAuthenticationToken.class).map(BearerTokenAuthenticationToken::getToken)
|
return Mono.justOrEmpty(authentication)
|
||||||
.flatMap(this.jwtDecoder::decode).flatMap(this.jwtAuthenticationConverter::convert)
|
.filter((a) -> a instanceof BearerTokenAuthenticationToken)
|
||||||
.cast(Authentication.class).onErrorMap(JwtException.class, this::onError);
|
.cast(BearerTokenAuthenticationToken.class)
|
||||||
|
.map(BearerTokenAuthenticationToken::getToken)
|
||||||
|
.flatMap(this.jwtDecoder::decode)
|
||||||
|
.flatMap(this.jwtAuthenticationConverter::convert)
|
||||||
|
.cast(Authentication.class)
|
||||||
|
.onErrorMap(JwtException.class, this::onError);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -75,19 +75,28 @@ public class OpaqueTokenReactiveAuthenticationManager implements ReactiveAuthent
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Authentication> authenticate(Authentication authentication) {
|
public Mono<Authentication> authenticate(Authentication authentication) {
|
||||||
return Mono.justOrEmpty(authentication).filter(BearerTokenAuthenticationToken.class::isInstance)
|
// @formatter:off
|
||||||
.cast(BearerTokenAuthenticationToken.class).map(BearerTokenAuthenticationToken::getToken)
|
return Mono.justOrEmpty(authentication)
|
||||||
.flatMap(this::authenticate).cast(Authentication.class);
|
.filter(BearerTokenAuthenticationToken.class::isInstance)
|
||||||
|
.cast(BearerTokenAuthenticationToken.class)
|
||||||
|
.map(BearerTokenAuthenticationToken::getToken)
|
||||||
|
.flatMap(this::authenticate)
|
||||||
|
.cast(Authentication.class);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<BearerTokenAuthentication> authenticate(String token) {
|
private Mono<BearerTokenAuthentication> authenticate(String token) {
|
||||||
return this.introspector.introspect(token).map((principal) -> {
|
// @formatter:off
|
||||||
Instant iat = principal.getAttribute(OAuth2IntrospectionClaimNames.ISSUED_AT);
|
return this.introspector.introspect(token)
|
||||||
Instant exp = principal.getAttribute(OAuth2IntrospectionClaimNames.EXPIRES_AT);
|
.map((principal) -> {
|
||||||
// construct token
|
Instant iat = principal.getAttribute(OAuth2IntrospectionClaimNames.ISSUED_AT);
|
||||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
|
Instant exp = principal.getAttribute(OAuth2IntrospectionClaimNames.EXPIRES_AT);
|
||||||
return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());
|
// construct token
|
||||||
}).onErrorMap(OAuth2IntrospectionException.class, this::onError);
|
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
|
||||||
|
return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());
|
||||||
|
})
|
||||||
|
.onErrorMap(OAuth2IntrospectionException.class, this::onError);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthenticationException onError(OAuth2IntrospectionException ex) {
|
private AuthenticationException onError(OAuth2IntrospectionException ex) {
|
||||||
|
|
|
@ -39,8 +39,11 @@ public final class ReactiveJwtAuthenticationConverter implements Converter<Jwt,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<AbstractAuthenticationToken> convert(Jwt jwt) {
|
public Mono<AbstractAuthenticationToken> convert(Jwt jwt) {
|
||||||
return this.jwtGrantedAuthoritiesConverter.convert(jwt).collectList()
|
// @formatter:off
|
||||||
|
return this.jwtGrantedAuthoritiesConverter.convert(jwt)
|
||||||
|
.collectList()
|
||||||
.map((authorities) -> new JwtAuthenticationToken(jwt, authorities));
|
.map((authorities) -> new JwtAuthenticationToken(jwt, authorities));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -90,25 +90,39 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {
|
public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {
|
||||||
return Mono.just(token).flatMap(this::makeRequest).flatMap(this::adaptToNimbusResponse)
|
// @formatter:off
|
||||||
.map(this::parseNimbusResponse).map(this::castToNimbusSuccess)
|
return Mono.just(token)
|
||||||
.doOnNext((response) -> validate(token, response)).map(this::convertClaimsSet)
|
.flatMap(this::makeRequest)
|
||||||
|
.flatMap(this::adaptToNimbusResponse)
|
||||||
|
.map(this::parseNimbusResponse)
|
||||||
|
.map(this::castToNimbusSuccess)
|
||||||
|
.doOnNext((response) -> validate(token, response))
|
||||||
|
.map(this::convertClaimsSet)
|
||||||
.onErrorMap((e) -> !(e instanceof OAuth2IntrospectionException), this::onError);
|
.onErrorMap((e) -> !(e instanceof OAuth2IntrospectionException), this::onError);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<ClientResponse> makeRequest(String token) {
|
private Mono<ClientResponse> makeRequest(String token) {
|
||||||
return this.webClient.post().uri(this.introspectionUri)
|
// @formatter:off
|
||||||
|
return this.webClient.post()
|
||||||
|
.uri(this.introspectionUri)
|
||||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE)
|
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
.body(BodyInserters.fromFormData("token", token)).exchange();
|
.body(BodyInserters.fromFormData("token", token))
|
||||||
|
.exchange();
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<HTTPResponse> adaptToNimbusResponse(ClientResponse responseEntity) {
|
private Mono<HTTPResponse> adaptToNimbusResponse(ClientResponse responseEntity) {
|
||||||
HTTPResponse response = new HTTPResponse(responseEntity.rawStatusCode());
|
HTTPResponse response = new HTTPResponse(responseEntity.rawStatusCode());
|
||||||
response.setHeader(HttpHeaders.CONTENT_TYPE, responseEntity.headers().contentType().get().toString());
|
response.setHeader(HttpHeaders.CONTENT_TYPE, responseEntity.headers().contentType().get().toString());
|
||||||
if (response.getStatusCode() != HTTPResponse.SC_OK) {
|
if (response.getStatusCode() != HTTPResponse.SC_OK) {
|
||||||
return responseEntity.bodyToFlux(DataBuffer.class).map(DataBufferUtils::release)
|
// @formatter:off
|
||||||
|
return responseEntity.bodyToFlux(DataBuffer.class)
|
||||||
|
.map(DataBufferUtils::release)
|
||||||
.then(Mono.error(new OAuth2IntrospectionException(
|
.then(Mono.error(new OAuth2IntrospectionException(
|
||||||
"Introspection endpoint responded with " + response.getStatusCode())));
|
"Introspection endpoint responded with " + response.getStatusCode()))
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
return responseEntity.bodyToMono(String.class).doOnNext(response::setContent).map((body) -> response);
|
return responseEntity.bodyToMono(String.class).doOnNext(response::setContent).map((body) -> response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,9 +59,13 @@ public class BearerTokenServerAccessDeniedHandler implements ServerAccessDeniedH
|
||||||
if (this.realmName != null) {
|
if (this.realmName != null) {
|
||||||
parameters.put("realm", this.realmName);
|
parameters.put("realm", this.realmName);
|
||||||
}
|
}
|
||||||
return exchange.getPrincipal().filter(AbstractOAuth2TokenAuthenticationToken.class::isInstance)
|
// @formatter:off
|
||||||
.map((token) -> errorMessageParameters(parameters)).switchIfEmpty(Mono.just(parameters))
|
return exchange.getPrincipal()
|
||||||
|
.filter(AbstractOAuth2TokenAuthenticationToken.class::isInstance)
|
||||||
|
.map((token) -> errorMessageParameters(parameters))
|
||||||
|
.switchIfEmpty(Mono.just(parameters))
|
||||||
.flatMap((params) -> respond(exchange, params));
|
.flatMap((params) -> respond(exchange, params));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -53,21 +53,35 @@ public final class ServerBearerExchangeFilterFunction implements ExchangeFilterF
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
|
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
|
||||||
return oauth2Token().map((token) -> bearer(request, token)).defaultIfEmpty(request).flatMap(next::exchange);
|
// @formatter:off
|
||||||
|
return oauth2Token().map((token) -> bearer(request, token))
|
||||||
|
.defaultIfEmpty(request)
|
||||||
|
.flatMap(next::exchange);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<AbstractOAuth2Token> oauth2Token() {
|
private Mono<AbstractOAuth2Token> oauth2Token() {
|
||||||
|
// @formatter:off
|
||||||
return currentAuthentication()
|
return currentAuthentication()
|
||||||
.filter((authentication) -> authentication.getCredentials() instanceof AbstractOAuth2Token)
|
.filter((authentication) -> authentication.getCredentials() instanceof AbstractOAuth2Token)
|
||||||
.map(Authentication::getCredentials).cast(AbstractOAuth2Token.class);
|
.map(Authentication::getCredentials)
|
||||||
|
.cast(AbstractOAuth2Token.class);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Authentication> currentAuthentication() {
|
private Mono<Authentication> currentAuthentication() {
|
||||||
return ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication);
|
// @formatter:off
|
||||||
|
return ReactiveSecurityContextHolder.getContext()
|
||||||
|
.map(SecurityContext::getAuthentication);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientRequest bearer(ClientRequest request, AbstractOAuth2Token token) {
|
private ClientRequest bearer(ClientRequest request, AbstractOAuth2Token token) {
|
||||||
return ClientRequest.from(request).headers((headers) -> headers.setBearerAuth(token.getTokenValue())).build();
|
// @formatter:off
|
||||||
|
return ClientRequest.from(request)
|
||||||
|
.headers((headers) -> headers.setBearerAuth(token.getTokenValue()))
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,13 +64,21 @@ public final class ServletBearerExchangeFilterFunction implements ExchangeFilter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
|
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
|
||||||
return oauth2Token().map((token) -> bearer(request, token)).defaultIfEmpty(request).flatMap(next::exchange);
|
// @formatter:off
|
||||||
|
return oauth2Token().map((token) -> bearer(request, token))
|
||||||
|
.defaultIfEmpty(request)
|
||||||
|
.flatMap(next::exchange);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<AbstractOAuth2Token> oauth2Token() {
|
private Mono<AbstractOAuth2Token> oauth2Token() {
|
||||||
return Mono.subscriberContext().flatMap(this::currentAuthentication)
|
// @formatter:off
|
||||||
|
return Mono.subscriberContext()
|
||||||
|
.flatMap(this::currentAuthentication)
|
||||||
.filter((authentication) -> authentication.getCredentials() instanceof AbstractOAuth2Token)
|
.filter((authentication) -> authentication.getCredentials() instanceof AbstractOAuth2Token)
|
||||||
.map(Authentication::getCredentials).cast(AbstractOAuth2Token.class);
|
.map(Authentication::getCredentials)
|
||||||
|
.cast(AbstractOAuth2Token.class);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Authentication> currentAuthentication(Context ctx) {
|
private Mono<Authentication> currentAuthentication(Context ctx) {
|
||||||
|
@ -88,7 +96,11 @@ public final class ServletBearerExchangeFilterFunction implements ExchangeFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientRequest bearer(ClientRequest request, AbstractOAuth2Token token) {
|
private ClientRequest bearer(ClientRequest request, AbstractOAuth2Token token) {
|
||||||
return ClientRequest.from(request).headers((headers) -> headers.setBearerAuth(token.getTokenValue())).build();
|
// @formatter:off
|
||||||
|
return ClientRequest.from(request)
|
||||||
|
.headers((headers) -> headers.setBearerAuth(token.getTokenValue()))
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,20 @@ public class BearerTokenAuthenticationTokenTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenTokenIsNullThenThrowsException() {
|
public void constructorWhenTokenIsNullThenThrowsException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenAuthenticationToken(null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenAuthenticationToken(null))
|
||||||
.withMessageContaining("token cannot be empty");
|
.withMessageContaining("token cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenTokenIsEmptyThenThrowsException() {
|
public void constructorWhenTokenIsEmptyThenThrowsException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenAuthenticationToken(""))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenAuthenticationToken(""))
|
||||||
.withMessageContaining("token cannot be empty");
|
.withMessageContaining("token cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -53,20 +53,29 @@ public class BearerTokenErrorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithErrorCodeAndHttpStatusWhenErrorCodeIsNullThenThrowIllegalArgumentException() {
|
public void constructorWithErrorCodeAndHttpStatusWhenErrorCodeIsNullThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenError(null, TEST_HTTP_STATUS, null, null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(null, TEST_HTTP_STATUS, null, null))
|
||||||
.withMessage("errorCode cannot be empty");
|
.withMessage("errorCode cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithErrorCodeAndHttpStatusWhenErrorCodeIsEmptyThenThrowIllegalArgumentException() {
|
public void constructorWithErrorCodeAndHttpStatusWhenErrorCodeIsEmptyThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenError("", TEST_HTTP_STATUS, null, null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError("", TEST_HTTP_STATUS, null, null))
|
||||||
.withMessage("errorCode cannot be empty");
|
.withMessage("errorCode cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithErrorCodeAndHttpStatusWhenHttpStatusIsNullThenThrowIllegalArgumentException() {
|
public void constructorWithErrorCodeAndHttpStatusWhenHttpStatusIsNullThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, null, null, null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, null, null, null))
|
||||||
.withMessage("httpStatus cannot be null");
|
.withMessage("httpStatus cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -82,52 +91,77 @@ public class BearerTokenErrorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithAllParametersWhenErrorCodeIsNullThenThrowIllegalArgumentException() {
|
public void constructorWithAllParametersWhenErrorCodeIsNullThenThrowIllegalArgumentException() {
|
||||||
|
// @formatter:off
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> new BearerTokenError(null, TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))
|
.isThrownBy(() -> new BearerTokenError(null, TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))
|
||||||
.withMessage("errorCode cannot be empty");
|
.withMessage("errorCode cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithAllParametersWhenErrorCodeIsEmptyThenThrowIllegalArgumentException() {
|
public void constructorWithAllParametersWhenErrorCodeIsEmptyThenThrowIllegalArgumentException() {
|
||||||
|
// @formatter:off
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> new BearerTokenError("", TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))
|
.isThrownBy(() -> new BearerTokenError("", TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))
|
||||||
.withMessage("errorCode cannot be empty");
|
.withMessage("errorCode cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithAllParametersWhenHttpStatusIsNullThenThrowIllegalArgumentException() {
|
public void constructorWithAllParametersWhenHttpStatusIsNullThenThrowIllegalArgumentException() {
|
||||||
|
// @formatter:off
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, null, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, null, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE))
|
||||||
.withMessage("httpStatus cannot be null");
|
.withMessage("httpStatus cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithAllParametersWhenErrorCodeIsInvalidThenThrowIllegalArgumentException() {
|
public void constructorWithAllParametersWhenErrorCodeIsInvalidThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE + "\"",
|
// @formatter:off
|
||||||
TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE)).withMessageContaining("errorCode")
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE + "\"",
|
||||||
|
TEST_HTTP_STATUS, TEST_DESCRIPTION, TEST_URI, TEST_SCOPE)
|
||||||
|
)
|
||||||
|
.withMessageContaining("errorCode")
|
||||||
.withMessageContaining("RFC 6750");
|
.withMessageContaining("RFC 6750");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithAllParametersWhenDescriptionIsInvalidThenThrowIllegalArgumentException() {
|
public void constructorWithAllParametersWhenDescriptionIsInvalidThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,
|
// @formatter:off
|
||||||
TEST_DESCRIPTION + "\"", TEST_URI, TEST_SCOPE)).withMessageContaining("description")
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,
|
||||||
|
TEST_DESCRIPTION + "\"", TEST_URI, TEST_SCOPE)
|
||||||
|
)
|
||||||
|
.withMessageContaining("description")
|
||||||
.withMessageContaining("RFC 6750");
|
.withMessageContaining("RFC 6750");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithAllParametersWhenErrorUriIsInvalidThenThrowIllegalArgumentException() {
|
public void constructorWithAllParametersWhenErrorUriIsInvalidThenThrowIllegalArgumentException() {
|
||||||
|
// @formatter:off
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, TEST_DESCRIPTION,
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS, TEST_DESCRIPTION,
|
||||||
TEST_URI + "\"", TEST_SCOPE))
|
TEST_URI + "\"", TEST_SCOPE)
|
||||||
.withMessageContaining("errorUri").withMessageContaining("RFC 6750");
|
)
|
||||||
|
.withMessageContaining("errorUri")
|
||||||
|
.withMessageContaining("RFC 6750");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWithAllParametersWhenScopeIsInvalidThenThrowIllegalArgumentException() {
|
public void constructorWithAllParametersWhenScopeIsInvalidThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,
|
// @formatter:off
|
||||||
TEST_DESCRIPTION, TEST_URI, TEST_SCOPE + "\"")).withMessageContaining("scope")
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenError(TEST_ERROR_CODE, TEST_HTTP_STATUS,
|
||||||
|
TEST_DESCRIPTION, TEST_URI, TEST_SCOPE + "\"")
|
||||||
|
)
|
||||||
|
.withMessageContaining("scope")
|
||||||
.withMessageContaining("RFC 6750");
|
.withMessageContaining("RFC 6750");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,20 +84,28 @@ public class BearerTokenAuthenticationTests {
|
||||||
@Test
|
@Test
|
||||||
public void getNameWhenTokenHasUsernameThenReturnsUsernameAttribute() {
|
public void getNameWhenTokenHasUsernameThenReturnsUsernameAttribute() {
|
||||||
BearerTokenAuthentication authenticated = new BearerTokenAuthentication(this.principal, this.token, null);
|
BearerTokenAuthentication authenticated = new BearerTokenAuthentication(this.principal, this.token, null);
|
||||||
|
// @formatter:off
|
||||||
assertThat(authenticated.getName())
|
assertThat(authenticated.getName())
|
||||||
.isEqualTo(this.principal.getAttribute(OAuth2IntrospectionClaimNames.SUBJECT));
|
.isEqualTo(this.principal.getAttribute(OAuth2IntrospectionClaimNames.SUBJECT));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenTokenIsNullThenThrowsException() {
|
public void constructorWhenTokenIsNullThenThrowsException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenAuthentication(this.principal, null, null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenAuthentication(this.principal, null, null))
|
||||||
.withMessageContaining("token cannot be null");
|
.withMessageContaining("token cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenCredentialIsNullThenThrowsException() {
|
public void constructorWhenCredentialIsNullThenThrowsException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new BearerTokenAuthentication(null, this.token, null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenAuthentication(null, this.token, null))
|
||||||
.withMessageContaining("principal cannot be null");
|
.withMessageContaining("principal cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -70,22 +70,29 @@ public class JwtAuthenticationConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenSettingNullPrincipalClaimName() {
|
public void whenSettingNullPrincipalClaimName() {
|
||||||
|
// @formatter:off
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(null))
|
.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(null))
|
||||||
.withMessage("principalClaimName cannot be empty");
|
.withMessage("principalClaimName cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenSettingEmptyPrincipalClaimName() {
|
public void whenSettingEmptyPrincipalClaimName() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(""))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(""))
|
||||||
.withMessage("principalClaimName cannot be empty");
|
.withMessage("principalClaimName cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenSettingBlankPrincipalClaimName() {
|
public void whenSettingBlankPrincipalClaimName() {
|
||||||
|
// @formatter:off
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(" "))
|
.isThrownBy(() -> this.jwtAuthenticationConverter.setPrincipalClaimName(" "))
|
||||||
.withMessage("principalClaimName cannot be empty");
|
.withMessage("principalClaimName cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -76,18 +76,22 @@ public class JwtAuthenticationProviderTests {
|
||||||
public void authenticateWhenJwtDecodeFailsThenRespondsWithInvalidToken() {
|
public void authenticateWhenJwtDecodeFailsThenRespondsWithInvalidToken() {
|
||||||
BearerTokenAuthenticationToken token = this.authentication();
|
BearerTokenAuthenticationToken token = this.authentication();
|
||||||
given(this.jwtDecoder.decode("token")).willThrow(BadJwtException.class);
|
given(this.jwtDecoder.decode("token")).willThrow(BadJwtException.class);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> this.provider.authenticate(token))
|
.isThrownBy(() -> this.provider.authenticate(token))
|
||||||
.matches(errorCode(BearerTokenErrorCodes.INVALID_TOKEN));
|
.matches(errorCode(BearerTokenErrorCodes.INVALID_TOKEN));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void authenticateWhenDecoderThrowsIncompatibleErrorMessageThenWrapsWithGenericOne() {
|
public void authenticateWhenDecoderThrowsIncompatibleErrorMessageThenWrapsWithGenericOne() {
|
||||||
BearerTokenAuthenticationToken token = this.authentication();
|
BearerTokenAuthenticationToken token = this.authentication();
|
||||||
given(this.jwtDecoder.decode(token.getToken())).willThrow(new BadJwtException("with \"invalid\" chars"));
|
given(this.jwtDecoder.decode(token.getToken())).willThrow(new BadJwtException("with \"invalid\" chars"));
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> this.provider.authenticate(token))
|
.isThrownBy(() -> this.provider.authenticate(token))
|
||||||
.satisfies((ex) -> assertThat(ex).hasFieldOrPropertyWithValue("error.description", "Invalid token"));
|
.satisfies((ex) -> assertThat(ex).hasFieldOrPropertyWithValue("error.description", "Invalid token"));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
// gh-7785
|
// gh-7785
|
||||||
|
@ -95,8 +99,11 @@ public class JwtAuthenticationProviderTests {
|
||||||
public void authenticateWhenDecoderFailsGenericallyThenThrowsGenericException() {
|
public void authenticateWhenDecoderFailsGenericallyThenThrowsGenericException() {
|
||||||
BearerTokenAuthenticationToken token = this.authentication();
|
BearerTokenAuthenticationToken token = this.authentication();
|
||||||
given(this.jwtDecoder.decode(token.getToken())).willThrow(new JwtException("no jwk set"));
|
given(this.jwtDecoder.decode(token.getToken())).willThrow(new JwtException("no jwk set"));
|
||||||
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(() -> this.provider.authenticate(token))
|
// @formatter:off
|
||||||
|
assertThatExceptionOfType(AuthenticationException.class)
|
||||||
|
.isThrownBy(() -> this.provider.authenticate(token))
|
||||||
.isNotInstanceOf(OAuth2AuthenticationException.class);
|
.isNotInstanceOf(OAuth2AuthenticationException.class);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -108,8 +115,11 @@ public class JwtAuthenticationProviderTests {
|
||||||
JwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt);
|
JwtAuthenticationToken authentication = new JwtAuthenticationToken(jwt);
|
||||||
given(this.jwtDecoder.decode(token.getToken())).willReturn(jwt);
|
given(this.jwtDecoder.decode(token.getToken())).willReturn(jwt);
|
||||||
given(this.jwtAuthenticationConverter.convert(jwt)).willReturn(authentication);
|
given(this.jwtAuthenticationConverter.convert(jwt)).willReturn(authentication);
|
||||||
assertThat(this.provider.authenticate(token)).isEqualTo(authentication).hasFieldOrPropertyWithValue("details",
|
// @formatter:off
|
||||||
|
assertThat(this.provider.authenticate(token))
|
||||||
|
.isEqualTo(authentication).hasFieldOrPropertyWithValue("details",
|
||||||
details);
|
details);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -37,7 +37,12 @@ public class JwtBearerTokenAuthenticationConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenJwtThenBearerTokenAuthentication() {
|
public void convertWhenJwtThenBearerTokenAuthentication() {
|
||||||
Jwt jwt = Jwt.withTokenValue("token-value").claim("claim", "value").header("header", "value").build();
|
// @formatter:off
|
||||||
|
Jwt jwt = Jwt.withTokenValue("token-value")
|
||||||
|
.claim("claim", "value")
|
||||||
|
.header("header", "value")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
AbstractAuthenticationToken token = this.converter.convert(jwt);
|
AbstractAuthenticationToken token = this.converter.convert(jwt);
|
||||||
assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
|
assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
|
||||||
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
|
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
|
||||||
|
@ -48,8 +53,12 @@ public class JwtBearerTokenAuthenticationConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenJwtWithScopeAttributeThenBearerTokenAuthentication() {
|
public void convertWhenJwtWithScopeAttributeThenBearerTokenAuthentication() {
|
||||||
Jwt jwt = Jwt.withTokenValue("token-value").claim("scope", "message:read message:write")
|
// @formatter:off
|
||||||
.header("header", "value").build();
|
Jwt jwt = Jwt.withTokenValue("token-value")
|
||||||
|
.claim("scope", "message:read message:write")
|
||||||
|
.header("header", "value")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
AbstractAuthenticationToken token = this.converter.convert(jwt);
|
AbstractAuthenticationToken token = this.converter.convert(jwt);
|
||||||
assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
|
assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
|
||||||
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
|
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
|
||||||
|
@ -59,8 +68,12 @@ public class JwtBearerTokenAuthenticationConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenJwtWithScpAttributeThenBearerTokenAuthentication() {
|
public void convertWhenJwtWithScpAttributeThenBearerTokenAuthentication() {
|
||||||
Jwt jwt = Jwt.withTokenValue("token-value").claim("scp", Arrays.asList("message:read", "message:write"))
|
// @formatter:off
|
||||||
.header("header", "value").build();
|
Jwt jwt = Jwt.withTokenValue("token-value")
|
||||||
|
.claim("scp", Arrays.asList("message:read", "message:write"))
|
||||||
|
.header("header", "value")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
AbstractAuthenticationToken token = this.converter.convert(jwt);
|
AbstractAuthenticationToken token = this.converter.convert(jwt);
|
||||||
assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
|
assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
|
||||||
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
|
BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
|
||||||
|
|
|
@ -45,7 +45,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
public void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scope", "message:read message:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
||||||
|
@ -54,7 +58,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWithCustomAuthorityPrefixWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
public void convertWithCustomAuthorityPrefixWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scope", "message:read message:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
|
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
@ -64,7 +72,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWithBlankAsCustomAuthorityPrefixWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
public void convertWithBlankAsCustomAuthorityPrefixWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scope", "message:read message:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
|
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
@ -74,7 +86,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {
|
public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scope", "").build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scope", "")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).isEmpty();
|
assertThat(authorities).isEmpty();
|
||||||
|
@ -82,7 +98,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", Arrays.asList("message:read", "message:write"))
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
||||||
|
@ -91,7 +111,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWithCustomAuthorityPrefixWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
public void convertWithCustomAuthorityPrefixWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", Arrays.asList("message:read", "message:write"))
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
|
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
@ -101,7 +125,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWithBlankAsCustomAuthorityPrefixWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
public void convertWithBlankAsCustomAuthorityPrefixWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", "message:read message:write").build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", "message:read message:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
|
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("");
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
@ -111,7 +139,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {
|
public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Collections.emptyList()).build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", Collections.emptyList())
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).isEmpty();
|
assertThat(authorities).isEmpty();
|
||||||
|
@ -119,8 +151,12 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasBothScopeAndScpThenScopeAttributeIsTranslatedToAuthorities() {
|
public void convertWhenTokenHasBothScopeAndScpThenScopeAttributeIsTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write"))
|
// @formatter:off
|
||||||
.claim("scope", "missive:read missive:write").build();
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", Arrays.asList("message:read", "message:write"))
|
||||||
|
.claim("scope", "missive:read missive:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_missive:read"),
|
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_missive:read"),
|
||||||
|
@ -129,8 +165,12 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {
|
public void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).claim("scope", "")
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", Arrays.asList("message:read", "message:write"))
|
||||||
|
.claim("scope", "")
|
||||||
.build();
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).isEmpty();
|
assertThat(authorities).isEmpty();
|
||||||
|
@ -138,15 +178,25 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasEmptyScopeAndEmptyScpAttributeThenTranslatesToNoAuthorities() {
|
public void convertWhenTokenHasEmptyScopeAndEmptyScpAttributeThenTranslatesToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Collections.emptyList()).claim("scope", Collections.emptyList()).build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", Collections.emptyList())
|
||||||
|
.claim("scope", Collections.emptyList())
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).isEmpty();
|
assertThat(authorities)
|
||||||
|
.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasNoScopeAndNoScpAttributeThenTranslatesToNoAuthorities() {
|
public void convertWhenTokenHasNoScopeAndNoScpAttributeThenTranslatesToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("roles", Arrays.asList("message:read", "message:write")).build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("roles", Arrays.asList("message:read", "message:write"))
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).isEmpty();
|
assertThat(authorities).isEmpty();
|
||||||
|
@ -154,16 +204,25 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasUnsupportedTypeForScopeThenTranslatesToNoAuthorities() {
|
public void convertWhenTokenHasUnsupportedTypeForScopeThenTranslatesToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scope", new String[] { "message:read", "message:write" }).build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scope", new String[] { "message:read", "message:write" })
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
assertThat(authorities).isEmpty();
|
assertThat(authorities)
|
||||||
|
.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToAuthorities() {
|
public void convertWhenTokenHasCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("roles", Arrays.asList("message:read", "message:write"))
|
// @formatter:off
|
||||||
.claim("scope", "missive:read missive:write").build();
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("roles", Arrays.asList("message:read", "message:write"))
|
||||||
|
.claim("scope", "missive:read missive:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
|
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
@ -173,8 +232,12 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasEmptyCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToNoAuthorities() {
|
public void convertWhenTokenHasEmptyCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("roles", Collections.emptyList()).claim("scope", "missive:read missive:write")
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("roles", Collections.emptyList())
|
||||||
|
.claim("scope", "missive:read missive:write")
|
||||||
.build();
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
|
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
@ -183,7 +246,11 @@ public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasNoCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToNoAuthorities() {
|
public void convertWhenTokenHasNoCustomClaimNameThenCustomClaimNameAttributeIsTranslatedToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scope", "missive:read missive:write").build();
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scope", "missive:read missive:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
|
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
|
||||||
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
|
|
@ -64,8 +64,12 @@ public class JwtIssuerAuthenticationManagerResolverTests {
|
||||||
try (MockWebServer server = new MockWebServer()) {
|
try (MockWebServer server = new MockWebServer()) {
|
||||||
server.start();
|
server.start();
|
||||||
String issuer = server.url("").toString();
|
String issuer = server.url("").toString();
|
||||||
server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
|
// @formatter:off
|
||||||
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
|
server.enqueue(new MockResponse().setResponseCode(200)
|
||||||
|
.setHeader("Content-Type", "application/json")
|
||||||
|
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)
|
||||||
|
));
|
||||||
|
// @formatter:on
|
||||||
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
|
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
|
||||||
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
|
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
|
||||||
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
|
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
|
||||||
|
@ -86,9 +90,11 @@ public class JwtIssuerAuthenticationManagerResolverTests {
|
||||||
"other", "issuers");
|
"other", "issuers");
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.addHeader("Authorization", "Bearer " + this.jwt);
|
request.addHeader("Authorization", "Bearer " + this.jwt);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
||||||
.withMessageContaining("Invalid issuer");
|
.withMessageContaining("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -108,16 +114,20 @@ public class JwtIssuerAuthenticationManagerResolverTests {
|
||||||
Map<String, AuthenticationManager> authenticationManagers = new HashMap<>();
|
Map<String, AuthenticationManager> authenticationManagers = new HashMap<>();
|
||||||
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
|
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
|
||||||
authenticationManagers::get);
|
authenticationManagers::get);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
||||||
.withMessageContaining("Invalid issuer");
|
.withMessageContaining("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
|
AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
|
||||||
authenticationManagers.put("trusted", authenticationManager);
|
authenticationManagers.put("trusted", authenticationManager);
|
||||||
assertThat(authenticationManagerResolver.resolve(request)).isSameAs(authenticationManager);
|
assertThat(authenticationManagerResolver.resolve(request)).isSameAs(authenticationManager);
|
||||||
authenticationManagers.clear();
|
authenticationManagers.clear();
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
||||||
.withMessageContaining("Invalid issuer");
|
.withMessageContaining("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -126,9 +136,11 @@ public class JwtIssuerAuthenticationManagerResolverTests {
|
||||||
"trusted");
|
"trusted");
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.addHeader("Authorization", "Bearer jwt");
|
request.addHeader("Authorization", "Bearer jwt");
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
||||||
.withMessageNotContaining("Invalid issuer");
|
.withMessageNotContaining("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -137,9 +149,11 @@ public class JwtIssuerAuthenticationManagerResolverTests {
|
||||||
"trusted");
|
"trusted");
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.addHeader("Authorization", "Bearer " + this.noIssuer);
|
request.addHeader("Authorization", "Bearer " + this.noIssuer);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
.isThrownBy(() -> authenticationManagerResolver.resolve(request))
|
||||||
.withMessageContaining("Missing issuer");
|
.withMessageContaining("Missing issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -148,8 +162,13 @@ public class JwtIssuerAuthenticationManagerResolverTests {
|
||||||
"trusted");
|
"trusted");
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.addHeader("Authorization", "Bearer " + this.evil);
|
request.addHeader("Authorization", "Bearer " + this.evil);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(request)).withMessage("Invalid issuer");
|
.isThrownBy(() -> authenticationManagerResolver
|
||||||
|
.resolve(request)
|
||||||
|
)
|
||||||
|
.withMessage("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -52,8 +52,12 @@ import static org.mockito.Mockito.mock;
|
||||||
*/
|
*/
|
||||||
public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||||
|
|
||||||
private static final String DEFAULT_RESPONSE_TEMPLATE = "{\n" + " \"issuer\": \"%s\", \n"
|
// @formatter:off
|
||||||
+ " \"jwks_uri\": \"%s/.well-known/jwks.json\" \n" + "}";
|
private static final String DEFAULT_RESPONSE_TEMPLATE = "{\n"
|
||||||
|
+ " \"issuer\": \"%s\", \n"
|
||||||
|
+ " \"jwks_uri\": \"%s/.well-known/jwks.json\" \n"
|
||||||
|
+ "}";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
private String jwt = jwt("iss", "trusted");
|
private String jwt = jwt("iss", "trusted");
|
||||||
|
|
||||||
|
@ -87,9 +91,11 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||||
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
|
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
|
||||||
"other", "issuers");
|
"other", "issuers");
|
||||||
MockServerWebExchange exchange = withBearerToken(this.jwt);
|
MockServerWebExchange exchange = withBearerToken(this.jwt);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
|
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
|
||||||
.withMessageContaining("Invalid issuer");
|
.withMessageContaining("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -114,9 +120,11 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||||
authenticationManagers.put("trusted", authenticationManager);
|
authenticationManagers.put("trusted", authenticationManager);
|
||||||
assertThat(authenticationManagerResolver.resolve(exchange).block()).isSameAs(authenticationManager);
|
assertThat(authenticationManagerResolver.resolve(exchange).block()).isSameAs(authenticationManager);
|
||||||
authenticationManagers.clear();
|
authenticationManagers.clear();
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
|
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
|
||||||
.withMessageContaining("Invalid issuer");
|
.withMessageContaining("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -124,9 +132,11 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||||
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
|
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
|
||||||
"trusted");
|
"trusted");
|
||||||
MockServerWebExchange exchange = withBearerToken("jwt");
|
MockServerWebExchange exchange = withBearerToken("jwt");
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
|
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
|
||||||
.withMessageNotContaining("Invalid issuer");
|
.withMessageNotContaining("Invalid issuer");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -144,8 +154,11 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||||
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
|
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
|
||||||
"trusted");
|
"trusted");
|
||||||
MockServerWebExchange exchange = withBearerToken(this.evil);
|
MockServerWebExchange exchange = withBearerToken(this.evil);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block()).withMessage("Invalid token");
|
.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
|
||||||
|
.withMessage("Invalid token");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -159,7 +172,7 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenNullAuthenticationManagerResolverThenException() {
|
public void constructorWhenNullAuthenticationManagerResolverThenException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(
|
assertThatIllegalArgumentException().isThrownBy(
|
||||||
() -> new JwtIssuerReactiveAuthenticationManagerResolver((ReactiveAuthenticationManagerResolver) null));
|
() -> new JwtIssuerReactiveAuthenticationManagerResolver((ReactiveAuthenticationManagerResolver) null));JwtReactiveAuthenticationManagerTests
|
||||||
}
|
}
|
||||||
|
|
||||||
private String jwt(String claim, String value) {
|
private String jwt(String claim, String value) {
|
||||||
|
@ -168,8 +181,11 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private MockServerWebExchange withBearerToken(String token) {
|
private MockServerWebExchange withBearerToken(String token) {
|
||||||
MockServerHttpRequest request = MockServerHttpRequest.get("/").header("Authorization", "Bearer " + token)
|
// @formatter:off
|
||||||
|
MockServerHttpRequest request = MockServerHttpRequest.get("/")
|
||||||
|
.header("Authorization", "Bearer " + token)
|
||||||
.build();
|
.build();
|
||||||
|
// @formatter:on
|
||||||
return MockServerWebExchange.from(request);
|
return MockServerWebExchange.from(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,13 +58,20 @@ public class JwtReactiveAuthenticationManagerTests {
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
this.manager = new JwtReactiveAuthenticationManager(this.jwtDecoder);
|
this.manager = new JwtReactiveAuthenticationManager(this.jwtDecoder);
|
||||||
this.jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
|
// @formatter:off
|
||||||
|
this.jwt = TestJwts.jwt()
|
||||||
|
.claim("scope", "message:read message:write")
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenJwtDecoderNullThenIllegalArgumentException() {
|
public void constructorWhenJwtDecoderNullThenIllegalArgumentException() {
|
||||||
this.jwtDecoder = null;
|
this.jwtDecoder = null;
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new JwtReactiveAuthenticationManager(this.jwtDecoder));
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new JwtReactiveAuthenticationManager(this.jwtDecoder));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -93,9 +100,13 @@ public class JwtReactiveAuthenticationManagerTests {
|
||||||
public void authenticateWhenDecoderThrowsIncompatibleErrorMessageThenWrapsWithGenericOne() {
|
public void authenticateWhenDecoderThrowsIncompatibleErrorMessageThenWrapsWithGenericOne() {
|
||||||
BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1");
|
BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1");
|
||||||
given(this.jwtDecoder.decode(token.getToken())).willThrow(new BadJwtException("with \"invalid\" chars"));
|
given(this.jwtDecoder.decode(token.getToken())).willThrow(new BadJwtException("with \"invalid\" chars"));
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
.isThrownBy(() -> this.manager.authenticate(token).block())
|
.isThrownBy(() -> this.manager.authenticate(token).block())
|
||||||
.satisfies((ex) -> assertThat(ex).hasFieldOrPropertyWithValue("error.description", "Invalid token"));
|
.satisfies((ex) -> assertThat(ex)
|
||||||
|
.hasFieldOrPropertyWithValue("error.description", "Invalid token")
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
// gh-7785
|
// gh-7785
|
||||||
|
@ -103,16 +114,21 @@ public class JwtReactiveAuthenticationManagerTests {
|
||||||
public void authenticateWhenDecoderFailsGenericallyThenThrowsGenericException() {
|
public void authenticateWhenDecoderFailsGenericallyThenThrowsGenericException() {
|
||||||
BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1");
|
BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1");
|
||||||
given(this.jwtDecoder.decode(token.getToken())).willThrow(new JwtException("no jwk set"));
|
given(this.jwtDecoder.decode(token.getToken())).willThrow(new JwtException("no jwk set"));
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(AuthenticationException.class)
|
assertThatExceptionOfType(AuthenticationException.class)
|
||||||
.isThrownBy(() -> this.manager.authenticate(token).block())
|
.isThrownBy(() -> this.manager.authenticate(token).block())
|
||||||
.isNotInstanceOf(OAuth2AuthenticationException.class);
|
.isNotInstanceOf(OAuth2AuthenticationException.class);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void authenticateWhenNotJwtExceptionThenPropagates() {
|
public void authenticateWhenNotJwtExceptionThenPropagates() {
|
||||||
BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1");
|
BearerTokenAuthenticationToken token = new BearerTokenAuthenticationToken("token-1");
|
||||||
given(this.jwtDecoder.decode(any())).willReturn(Mono.error(new RuntimeException("Oops")));
|
given(this.jwtDecoder.decode(any())).willReturn(Mono.error(new RuntimeException("Oops")));
|
||||||
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> this.manager.authenticate(token).block());
|
// @formatter:off
|
||||||
|
assertThatExceptionOfType(RuntimeException.class)
|
||||||
|
.isThrownBy(() -> this.manager.authenticate(token).block());
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -122,8 +138,11 @@ public class JwtReactiveAuthenticationManagerTests {
|
||||||
Authentication authentication = this.manager.authenticate(token).block();
|
Authentication authentication = this.manager.authenticate(token).block();
|
||||||
assertThat(authentication).isNotNull();
|
assertThat(authentication).isNotNull();
|
||||||
assertThat(authentication.isAuthenticated()).isTrue();
|
assertThat(authentication.isAuthenticated()).isTrue();
|
||||||
assertThat(authentication.getAuthorities()).extracting(GrantedAuthority::getAuthority)
|
// @formatter:off
|
||||||
|
assertThat(authentication.getAuthorities())
|
||||||
|
.extracting(GrantedAuthority::getAuthority)
|
||||||
.containsOnly("SCOPE_message:read", "SCOPE_message:write");
|
.containsOnly("SCOPE_message:read", "SCOPE_message:write");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,10 @@ public class OpaqueTokenAuthenticationProviderTests {
|
||||||
Authentication result = provider.authenticate(new BearerTokenAuthenticationToken("token"));
|
Authentication result = provider.authenticate(new BearerTokenAuthenticationToken("token"));
|
||||||
assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);
|
assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);
|
||||||
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
|
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
|
||||||
assertThat(attributes).isNotNull().containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
// @formatter:off
|
||||||
|
assertThat(attributes)
|
||||||
|
.isNotNull()
|
||||||
|
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
||||||
Arrays.asList("https://protected.example.net/resource"))
|
Arrays.asList("https://protected.example.net/resource"))
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
||||||
|
@ -69,8 +72,11 @@ public class OpaqueTokenAuthenticationProviderTests {
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
||||||
.containsEntry("extension_field", "twenty-seven");
|
.containsEntry("extension_field", "twenty-seven");
|
||||||
assertThat(result.getAuthorities()).extracting("authority").containsExactly("SCOPE_read", "SCOPE_write",
|
assertThat(result.getAuthorities())
|
||||||
|
.extracting("authority")
|
||||||
|
.containsExactly("SCOPE_read", "SCOPE_write",
|
||||||
"SCOPE_dolphin");
|
"SCOPE_dolphin");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -83,7 +89,11 @@ public class OpaqueTokenAuthenticationProviderTests {
|
||||||
Authentication result = provider.authenticate(new BearerTokenAuthenticationToken("token"));
|
Authentication result = provider.authenticate(new BearerTokenAuthenticationToken("token"));
|
||||||
assertThat(result.getPrincipal()).isInstanceOf(OAuth2AuthenticatedPrincipal.class);
|
assertThat(result.getPrincipal()).isInstanceOf(OAuth2AuthenticatedPrincipal.class);
|
||||||
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
|
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
|
||||||
assertThat(attributes).isNotNull().doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
|
// @formatter:off
|
||||||
|
assertThat(attributes)
|
||||||
|
.isNotNull()
|
||||||
|
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
|
||||||
|
// @formatter:on
|
||||||
assertThat(result.getAuthorities()).isEmpty();
|
assertThat(result.getAuthorities()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +108,10 @@ public class OpaqueTokenAuthenticationProviderTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
|
public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new OpaqueTokenAuthenticationProvider(null));
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new OpaqueTokenAuthenticationProvider(null));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,10 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
|
||||||
Authentication result = provider.authenticate(new BearerTokenAuthenticationToken("token")).block();
|
Authentication result = provider.authenticate(new BearerTokenAuthenticationToken("token")).block();
|
||||||
assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);
|
assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);
|
||||||
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
|
Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
|
||||||
assertThat(attributes).isNotNull().containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
// @formatter:off
|
||||||
|
assertThat(attributes)
|
||||||
|
.isNotNull()
|
||||||
|
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
||||||
Arrays.asList("https://protected.example.net/resource"))
|
Arrays.asList("https://protected.example.net/resource"))
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
||||||
|
@ -70,8 +73,11 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
||||||
.containsEntry("extension_field", "twenty-seven");
|
.containsEntry("extension_field", "twenty-seven");
|
||||||
assertThat(result.getAuthorities()).extracting("authority").containsExactly("SCOPE_read", "SCOPE_write",
|
assertThat(result.getAuthorities())
|
||||||
|
.extracting("authority")
|
||||||
|
.containsExactly("SCOPE_read", "SCOPE_write",
|
||||||
"SCOPE_dolphin");
|
"SCOPE_dolphin");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -100,7 +106,10 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
|
public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new OpaqueTokenReactiveAuthenticationManager(null));
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new OpaqueTokenReactiveAuthenticationManager(null));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,11 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
|
Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
// @formatter:off
|
||||||
new SimpleGrantedAuthority("SCOPE_message:write"));
|
assertThat(authorities)
|
||||||
|
.containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:write"));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -64,8 +67,11 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).build();
|
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).build();
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
// @formatter:off
|
||||||
new SimpleGrantedAuthority("SCOPE_message:write"));
|
assertThat(authorities)
|
||||||
|
.containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:write"));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -82,14 +88,21 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
|
||||||
.claim("scope", "missive:read missive:write").build();
|
.claim("scope", "missive:read missive:write").build();
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_missive:read"),
|
// @formatter:off
|
||||||
new SimpleGrantedAuthority("SCOPE_missive:write"));
|
assertThat(authorities)
|
||||||
|
.containsExactly(new SimpleGrantedAuthority("SCOPE_missive:read"),
|
||||||
|
new SimpleGrantedAuthority("SCOPE_missive:write"));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {
|
public void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {
|
||||||
Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).claim("scope", "")
|
// @formatter:off
|
||||||
|
Jwt jwt = TestJwts.jwt()
|
||||||
|
.claim("scp", Arrays.asList("message:read", "message:write"))
|
||||||
|
.claim("scope", "")
|
||||||
.build();
|
.build();
|
||||||
|
// @formatter:on
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
assertThat(authorities).containsExactly();
|
assertThat(authorities).containsExactly();
|
||||||
|
|
|
@ -51,8 +51,11 @@ public class ReactiveJwtGrantedAuthoritiesConverterAdapterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenConstructingWithInvalidConverter() {
|
public void whenConstructingWithInvalidConverter() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new ReactiveJwtGrantedAuthoritiesConverterAdapter(null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new ReactiveJwtGrantedAuthoritiesConverterAdapter(null))
|
||||||
.withMessage("grantedAuthoritiesConverter cannot be null");
|
.withMessage("grantedAuthoritiesConverter cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,31 +62,62 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
|
|
||||||
private static final String CLIENT_SECRET = "secret";
|
private static final String CLIENT_SECRET = "secret";
|
||||||
|
|
||||||
private static final String ACTIVE_RESPONSE = "{\n" + " \"active\": true,\n"
|
// @formatter:off
|
||||||
+ " \"client_id\": \"l238j323ds-23ij4\",\n" + " \"username\": \"jdoe\",\n"
|
private static final String ACTIVE_RESPONSE = "{\n"
|
||||||
+ " \"scope\": \"read write dolphin\",\n" + " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
+ " \"active\": true,\n"
|
||||||
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
+ " \"client_id\": \"l238j323ds-23ij4\",\n"
|
||||||
+ " \"iss\": \"https://server.example.com/\",\n" + " \"exp\": 1419356238,\n"
|
+ " \"username\": \"jdoe\",\n"
|
||||||
+ " \"iat\": 1419350238,\n" + " \"extension_field\": \"twenty-seven\"\n" + " }";
|
+ " \"scope\": \"read write dolphin\",\n"
|
||||||
|
|
||||||
private static final String INACTIVE_RESPONSE = "{\n" + " \"active\": false\n" + " }";
|
|
||||||
|
|
||||||
private static final String INVALID_RESPONSE = "{\n" + " \"client_id\": \"l238j323ds-23ij4\",\n"
|
|
||||||
+ " \"username\": \"jdoe\",\n" + " \"scope\": \"read write dolphin\",\n"
|
|
||||||
+ " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
+ " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
||||||
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
||||||
+ " \"iss\": \"https://server.example.com/\",\n" + " \"exp\": 1419356238,\n"
|
+ " \"iss\": \"https://server.example.com/\",\n"
|
||||||
+ " \"iat\": 1419350238,\n" + " \"extension_field\": \"twenty-seven\"\n" + " }";
|
+ " \"exp\": 1419356238,\n"
|
||||||
|
+ " \"iat\": 1419350238,\n"
|
||||||
|
+ " \"extension_field\": \"twenty-seven\"\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
private static final String MALFORMED_ISSUER_RESPONSE = "{\n" + " \"active\" : \"true\",\n"
|
// @formatter:off
|
||||||
+ " \"iss\" : \"badissuer\"\n" + " }";
|
private static final String INACTIVE_RESPONSE = "{\n"
|
||||||
|
+ " \"active\": false\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
private static final String MALFORMED_SCOPE_RESPONSE = "{\n" + " \"active\": true,\n"
|
// @formatter:off
|
||||||
+ " \"client_id\": \"l238j323ds-23ij4\",\n" + " \"username\": \"jdoe\",\n"
|
private static final String INVALID_RESPONSE = "{\n"
|
||||||
+ " \"scope\": [ \"read\", \"write\", \"dolphin\" ],\n" + " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
+ " \"client_id\": \"l238j323ds-23ij4\",\n"
|
||||||
|
+ " \"username\": \"jdoe\",\n"
|
||||||
|
+ " \"scope\": \"read write dolphin\",\n"
|
||||||
|
+ " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
||||||
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
||||||
+ " \"iss\": \"https://server.example.com/\",\n" + " \"exp\": 1419356238,\n"
|
+ " \"iss\": \"https://server.example.com/\",\n"
|
||||||
+ " \"iat\": 1419350238,\n" + " \"extension_field\": \"twenty-seven\"\n" + " }";
|
+ " \"exp\": 1419356238,\n"
|
||||||
|
+ " \"iat\": 1419350238,\n"
|
||||||
|
+ " \"extension_field\": \"twenty-seven\"\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
private static final String MALFORMED_ISSUER_RESPONSE = "{\n"
|
||||||
|
+ " \"active\" : \"true\",\n"
|
||||||
|
+ " \"iss\" : \"badissuer\"\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
private static final String MALFORMED_SCOPE_RESPONSE = "{\n"
|
||||||
|
+ " \"active\": true,\n"
|
||||||
|
+ " \"client_id\": \"l238j323ds-23ij4\",\n"
|
||||||
|
+ " \"username\": \"jdoe\",\n"
|
||||||
|
+ " \"scope\": [ \"read\", \"write\", \"dolphin\" ],\n"
|
||||||
|
+ " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
||||||
|
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
||||||
|
+ " \"iss\": \"https://server.example.com/\",\n"
|
||||||
|
+ " \"exp\": 1419356238,\n"
|
||||||
|
+ " \"iat\": 1419350238,\n"
|
||||||
|
+ " \"extension_field\": \"twenty-seven\"\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
private static final ResponseEntity<String> ACTIVE = response(ACTIVE_RESPONSE);
|
private static final ResponseEntity<String> ACTIVE = response(ACTIVE_RESPONSE);
|
||||||
|
|
||||||
|
@ -106,7 +137,10 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
OpaqueTokenIntrospector introspectionClient = new NimbusOpaqueTokenIntrospector(introspectUri, CLIENT_ID,
|
OpaqueTokenIntrospector introspectionClient = new NimbusOpaqueTokenIntrospector(introspectUri, CLIENT_ID,
|
||||||
CLIENT_SECRET);
|
CLIENT_SECRET);
|
||||||
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token");
|
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token");
|
||||||
assertThat(authority.getAttributes()).isNotNull().containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
// @formatter:off
|
||||||
|
assertThat(authority.getAttributes())
|
||||||
|
.isNotNull()
|
||||||
|
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
||||||
Arrays.asList("https://protected.example.net/resource"))
|
Arrays.asList("https://protected.example.net/resource"))
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
||||||
|
@ -116,6 +150,7 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
||||||
.containsEntry("extension_field", "twenty-seven");
|
.containsEntry("extension_field", "twenty-seven");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,8 +172,11 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
OpaqueTokenIntrospector introspectionClient = new NimbusOpaqueTokenIntrospector(INTROSPECTION_URL,
|
OpaqueTokenIntrospector introspectionClient = new NimbusOpaqueTokenIntrospector(INTROSPECTION_URL,
|
||||||
restOperations);
|
restOperations);
|
||||||
given(restOperations.exchange(any(RequestEntity.class), eq(String.class))).willReturn(INACTIVE);
|
given(restOperations.exchange(any(RequestEntity.class), eq(String.class))).willReturn(INACTIVE);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
||||||
.isThrownBy(() -> introspectionClient.introspect("token")).withMessage("Provided token isn't active");
|
.isThrownBy(() -> introspectionClient.introspect("token"))
|
||||||
|
.withMessage("Provided token isn't active");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -153,11 +191,15 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
|
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
|
||||||
.willReturn(response(new JSONObject(introspectedValues).toJSONString()));
|
.willReturn(response(new JSONObject(introspectedValues).toJSONString()));
|
||||||
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token");
|
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token");
|
||||||
assertThat(authority.getAttributes()).isNotNull().containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
// @formatter:off
|
||||||
|
assertThat(authority.getAttributes())
|
||||||
|
.isNotNull()
|
||||||
|
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"))
|
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"))
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
|
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
|
||||||
.doesNotContainKey(OAuth2IntrospectionClaimNames.CLIENT_ID)
|
.doesNotContainKey(OAuth2IntrospectionClaimNames.CLIENT_ID)
|
||||||
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
|
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -167,8 +209,11 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
restOperations);
|
restOperations);
|
||||||
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
|
given(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
|
||||||
.willThrow(new IllegalStateException("server was unresponsive"));
|
.willThrow(new IllegalStateException("server was unresponsive"));
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
||||||
.isThrownBy(() -> introspectionClient.introspect("token")).withMessage("server was unresponsive");
|
.isThrownBy(() -> introspectionClient.introspect("token"))
|
||||||
|
.withMessage("server was unresponsive");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -274,8 +319,12 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
@Override
|
@Override
|
||||||
public MockResponse dispatch(RecordedRequest request) {
|
public MockResponse dispatch(RecordedRequest request) {
|
||||||
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
|
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
return Optional.ofNullable(authorization).filter((a) -> isAuthorized(authorization, username, password))
|
// @formatter:off
|
||||||
.map((a) -> ok(response)).orElse(unauthorized());
|
return Optional.ofNullable(authorization)
|
||||||
|
.filter((a) -> isAuthorized(authorization, username, password))
|
||||||
|
.map((a) -> ok(response))
|
||||||
|
.orElse(unauthorized());
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -286,8 +335,10 @@ public class NimbusOpaqueTokenIntrospectorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MockResponse ok(String response) {
|
private static MockResponse ok(String response) {
|
||||||
return new MockResponse().setBody(response).setHeader(HttpHeaders.CONTENT_TYPE,
|
// @formatter:off
|
||||||
MediaType.APPLICATION_JSON_VALUE);
|
return new MockResponse().setBody(response)
|
||||||
|
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MockResponse unauthorized() {
|
private static MockResponse unauthorized() {
|
||||||
|
|
|
@ -58,24 +58,47 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
|
|
||||||
private static final String CLIENT_SECRET = "secret";
|
private static final String CLIENT_SECRET = "secret";
|
||||||
|
|
||||||
private static final String ACTIVE_RESPONSE = "{\n" + " \"active\": true,\n"
|
// @formatter:off
|
||||||
+ " \"client_id\": \"l238j323ds-23ij4\",\n" + " \"username\": \"jdoe\",\n"
|
private static final String ACTIVE_RESPONSE = "{\n"
|
||||||
+ " \"scope\": \"read write dolphin\",\n" + " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
+ " \"active\": true,\n"
|
||||||
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
+ " \"client_id\": \"l238j323ds-23ij4\",\n"
|
||||||
+ " \"iss\": \"https://server.example.com/\",\n" + " \"exp\": 1419356238,\n"
|
+ " \"username\": \"jdoe\",\n"
|
||||||
+ " \"iat\": 1419350238,\n" + " \"extension_field\": \"twenty-seven\"\n" + " }";
|
+ " \"scope\": \"read write dolphin\",\n"
|
||||||
|
|
||||||
private static final String INACTIVE_RESPONSE = "{\n" + " \"active\": false\n" + " }";
|
|
||||||
|
|
||||||
private static final String INVALID_RESPONSE = "{\n" + " \"client_id\": \"l238j323ds-23ij4\",\n"
|
|
||||||
+ " \"username\": \"jdoe\",\n" + " \"scope\": \"read write dolphin\",\n"
|
|
||||||
+ " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
+ " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
||||||
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
||||||
+ " \"iss\": \"https://server.example.com/\",\n" + " \"exp\": 1419356238,\n"
|
+ " \"iss\": \"https://server.example.com/\",\n"
|
||||||
+ " \"iat\": 1419350238,\n" + " \"extension_field\": \"twenty-seven\"\n" + " }";
|
+ " \"exp\": 1419356238,\n"
|
||||||
|
+ " \"iat\": 1419350238,\n"
|
||||||
|
+ " \"extension_field\": \"twenty-seven\"\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
private static final String MALFORMED_ISSUER_RESPONSE = "{\n" + " \"active\" : \"true\",\n"
|
// @formatter:off
|
||||||
+ " \"iss\" : \"badissuer\"\n" + " }";
|
private static final String INACTIVE_RESPONSE = "{\n"
|
||||||
|
+ " \"active\": false\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
private static final String INVALID_RESPONSE = "{\n"
|
||||||
|
+ " \"client_id\": \"l238j323ds-23ij4\",\n"
|
||||||
|
+ " \"username\": \"jdoe\",\n"
|
||||||
|
+ " \"scope\": \"read write dolphin\",\n"
|
||||||
|
+ " \"sub\": \"Z5O3upPC88QrAjx00dis\",\n"
|
||||||
|
+ " \"aud\": \"https://protected.example.net/resource\",\n"
|
||||||
|
+ " \"iss\": \"https://server.example.com/\",\n"
|
||||||
|
+ " \"exp\": 1419356238,\n"
|
||||||
|
+ " \"iat\": 1419350238,\n"
|
||||||
|
+ " \"extension_field\": \"twenty-seven\"\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
private static final String MALFORMED_ISSUER_RESPONSE = "{\n"
|
||||||
|
+ " \"active\" : \"true\",\n"
|
||||||
|
+ " \"iss\" : \"badissuer\"\n"
|
||||||
|
+ " }";
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void authenticateWhenActiveTokenThenOk() throws Exception {
|
public void authenticateWhenActiveTokenThenOk() throws Exception {
|
||||||
|
@ -85,7 +108,10 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
||||||
introspectUri, CLIENT_ID, CLIENT_SECRET);
|
introspectUri, CLIENT_ID, CLIENT_SECRET);
|
||||||
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token").block();
|
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token").block();
|
||||||
assertThat(authority.getAttributes()).isNotNull().containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
// @formatter:off
|
||||||
|
assertThat(authority.getAttributes())
|
||||||
|
.isNotNull()
|
||||||
|
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE,
|
||||||
Arrays.asList("https://protected.example.net/resource"))
|
Arrays.asList("https://protected.example.net/resource"))
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
.containsEntry(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4")
|
||||||
|
@ -95,6 +121,7 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
.containsEntry(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis")
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
.containsEntry(OAuth2IntrospectionClaimNames.USERNAME, "jdoe")
|
||||||
.containsEntry("extension_field", "twenty-seven");
|
.containsEntry("extension_field", "twenty-seven");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,11 +158,15 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
||||||
INTROSPECTION_URL, webClient);
|
INTROSPECTION_URL, webClient);
|
||||||
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token").block();
|
OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token").block();
|
||||||
assertThat(authority.getAttributes()).isNotNull().containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
// @formatter:off
|
||||||
|
assertThat(authority.getAttributes())
|
||||||
|
.isNotNull()
|
||||||
|
.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"))
|
.containsEntry(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("aud"))
|
||||||
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
|
.containsEntry(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L))
|
||||||
.doesNotContainKey(OAuth2IntrospectionClaimNames.CLIENT_ID)
|
.doesNotContainKey(OAuth2IntrospectionClaimNames.CLIENT_ID)
|
||||||
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
|
.doesNotContainKey(OAuth2IntrospectionClaimNames.SCOPE);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -143,9 +174,11 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
WebClient webClient = mockResponse(new IllegalStateException("server was unresponsive"));
|
WebClient webClient = mockResponse(new IllegalStateException("server was unresponsive"));
|
||||||
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
||||||
INTROSPECTION_URL, webClient);
|
INTROSPECTION_URL, webClient);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
||||||
.isThrownBy(() -> introspectionClient.introspect("token").block())
|
.isThrownBy(() -> introspectionClient.introspect("token").block())
|
||||||
.withMessage("server was unresponsive");
|
.withMessage("server was unresponsive");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -162,8 +195,10 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
WebClient webClient = mockResponse(INVALID_RESPONSE);
|
WebClient webClient = mockResponse(INVALID_RESPONSE);
|
||||||
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
NimbusReactiveOpaqueTokenIntrospector introspectionClient = new NimbusReactiveOpaqueTokenIntrospector(
|
||||||
INTROSPECTION_URL, webClient);
|
INTROSPECTION_URL, webClient);
|
||||||
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
assertThatExceptionOfType(OAuth2IntrospectionException.class)
|
||||||
.isThrownBy(() -> introspectionClient.introspect("token").block());
|
.isThrownBy(() -> introspectionClient.introspect("token").block());
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -229,8 +264,12 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
@Override
|
@Override
|
||||||
public MockResponse dispatch(RecordedRequest request) {
|
public MockResponse dispatch(RecordedRequest request) {
|
||||||
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
|
String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
return Optional.ofNullable(authorization).filter((a) -> isAuthorized(authorization, username, password))
|
// @formatter:off
|
||||||
.map((a) -> ok(response)).orElse(unauthorized());
|
return Optional.ofNullable(authorization)
|
||||||
|
.filter((a) -> isAuthorized(authorization, username, password))
|
||||||
|
.map((a) -> ok(response))
|
||||||
|
.orElse(unauthorized());
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -241,8 +280,10 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MockResponse ok(String response) {
|
private static MockResponse ok(String response) {
|
||||||
return new MockResponse().setBody(response).setHeader(HttpHeaders.CONTENT_TYPE,
|
// @formatter:off
|
||||||
MediaType.APPLICATION_JSON_VALUE);
|
return new MockResponse().setBody(response)
|
||||||
|
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MockResponse unauthorized() {
|
private static MockResponse unauthorized() {
|
||||||
|
|
|
@ -157,29 +157,39 @@ public class BearerTokenAuthenticationFilterTests {
|
||||||
@Test
|
@Test
|
||||||
public void setAuthenticationEntryPointWhenNullThenThrowsException() {
|
public void setAuthenticationEntryPointWhenNullThenThrowsException() {
|
||||||
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
|
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> filter.setAuthenticationEntryPoint(null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> filter.setAuthenticationEntryPoint(null))
|
||||||
.withMessageContaining("authenticationEntryPoint cannot be null");
|
.withMessageContaining("authenticationEntryPoint cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setBearerTokenResolverWhenNullThenThrowsException() {
|
public void setBearerTokenResolverWhenNullThenThrowsException() {
|
||||||
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
|
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(this.authenticationManager);
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> filter.setBearerTokenResolver(null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> filter.setBearerTokenResolver(null))
|
||||||
.withMessageContaining("bearerTokenResolver cannot be null");
|
.withMessageContaining("bearerTokenResolver cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenNullAuthenticationManagerThenThrowsException() {
|
public void constructorWhenNullAuthenticationManagerThenThrowsException() {
|
||||||
|
// @formatter:off
|
||||||
assertThatIllegalArgumentException()
|
assertThatIllegalArgumentException()
|
||||||
.isThrownBy(() -> new BearerTokenAuthenticationFilter((AuthenticationManager) null))
|
.isThrownBy(() -> new BearerTokenAuthenticationFilter((AuthenticationManager) null))
|
||||||
.withMessageContaining("authenticationManager cannot be null");
|
.withMessageContaining("authenticationManager cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenNullAuthenticationManagerResolverThenThrowsException() {
|
public void constructorWhenNullAuthenticationManagerResolverThenThrowsException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(
|
// @formatter:off
|
||||||
() -> new BearerTokenAuthenticationFilter((AuthenticationManagerResolver<HttpServletRequest>) null))
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new BearerTokenAuthenticationFilter((AuthenticationManagerResolver<HttpServletRequest>) null))
|
||||||
.withMessageContaining("authenticationManagerResolver cannot be null");
|
.withMessageContaining("authenticationManagerResolver cannot be null");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
private BearerTokenAuthenticationFilter addMocks(BearerTokenAuthenticationFilter filter) {
|
private BearerTokenAuthenticationFilter addMocks(BearerTokenAuthenticationFilter filter) {
|
||||||
|
|
|
@ -38,14 +38,20 @@ public class HeaderBearerTokenResolverTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenHeaderNullThenThrowIllegalArgumentException() {
|
public void constructorWhenHeaderNullThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new HeaderBearerTokenResolver(null))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new HeaderBearerTokenResolver(null))
|
||||||
.withMessage("header cannot be empty");
|
.withMessage("header cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructorWhenHeaderEmptyThenThrowIllegalArgumentException() {
|
public void constructorWhenHeaderEmptyThenThrowIllegalArgumentException() {
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> new HeaderBearerTokenResolver(""))
|
// @formatter:off
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new HeaderBearerTokenResolver(""))
|
||||||
.withMessage("header cannot be empty");
|
.withMessage("header cannot be empty");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -77,9 +77,12 @@ public class BearerTokenAccessDeniedHandlerTests {
|
||||||
request.setUserPrincipal(token);
|
request.setUserPrincipal(token);
|
||||||
this.accessDeniedHandler.handle(request, response, null);
|
this.accessDeniedHandler.handle(request, response, null);
|
||||||
assertThat(response.getStatus()).isEqualTo(403);
|
assertThat(response.getStatus()).isEqualTo(403);
|
||||||
assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\", "
|
// @formatter:off
|
||||||
+ "error_description=\"The request requires higher privileges than provided by the access token.\", "
|
assertThat(response.getHeader("WWW-Authenticate"))
|
||||||
+ "error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"");
|
.isEqualTo("Bearer error=\"insufficient_scope\", "
|
||||||
|
+ "error_description=\"The request requires higher privileges than provided by the access token.\", "
|
||||||
|
+ "error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\"");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -77,10 +77,12 @@ public class BearerTokenServerAccessDeniedHandlerTests {
|
||||||
given(exchange.getResponse()).willReturn(new MockServerHttpResponse());
|
given(exchange.getResponse()).willReturn(new MockServerHttpResponse());
|
||||||
this.accessDeniedHandler.handle(exchange, null).block();
|
this.accessDeniedHandler.handle(exchange, null).block();
|
||||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||||
|
// @formatter:off
|
||||||
assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate"))
|
assertThat(exchange.getResponse().getHeaders().get("WWW-Authenticate"))
|
||||||
.isEqualTo(Arrays.asList("Bearer error=\"insufficient_scope\", "
|
.isEqualTo(Arrays.asList("Bearer error=\"insufficient_scope\", "
|
||||||
+ "error_description=\"The request requires higher privileges than provided by the access token.\", "
|
+ "error_description=\"The request requires higher privileges than provided by the access token.\", "
|
||||||
+ "error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\""));
|
+ "error_uri=\"https://tools.ietf.org/html/rfc6750#section-3.1\""));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -52,8 +52,10 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenValidHeaderIsPresentThenTokenIsResolved() {
|
public void resolveWhenValidHeaderIsPresentThenTokenIsResolved() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
// @formatter:off
|
||||||
"Bearer " + TEST_TOKEN);
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_TOKEN);
|
||||||
|
// @formatter:on
|
||||||
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,16 +63,20 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenHeaderEndsWithPaddingIndicatorThenTokenIsResolved() {
|
public void resolveWhenHeaderEndsWithPaddingIndicatorThenTokenIsResolved() {
|
||||||
String token = TEST_TOKEN + "==";
|
String token = TEST_TOKEN + "==";
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
// @formatter:off
|
||||||
"Bearer " + token);
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
||||||
|
// @formatter:on
|
||||||
assertThat(convertToToken(request).getToken()).isEqualTo(token);
|
assertThat(convertToToken(request).getToken()).isEqualTo(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenCustomDefinedHeaderIsValidAndPresentThenTokenIsResolved() {
|
public void resolveWhenCustomDefinedHeaderIsValidAndPresentThenTokenIsResolved() {
|
||||||
this.converter.setBearerTokenHeaderName(CUSTOM_HEADER);
|
this.converter.setBearerTokenHeaderName(CUSTOM_HEADER);
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(CUSTOM_HEADER,
|
// @formatter:off
|
||||||
"Bearer " + TEST_TOKEN);
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.header(CUSTOM_HEADER, "Bearer " + TEST_TOKEN);
|
||||||
|
// @formatter:on
|
||||||
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,19 +85,24 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
||||||
public void resolveWhenValidHeaderIsEmptyStringThenTokenIsResolved() {
|
public void resolveWhenValidHeaderIsEmptyStringThenTokenIsResolved() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
||||||
"Bearer ");
|
"Bearer ");
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))
|
// @formatter:off
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
|
.isThrownBy(() -> convertToToken(request))
|
||||||
.satisfies((ex) -> {
|
.satisfies((ex) -> {
|
||||||
BearerTokenError error = (BearerTokenError) ex.getError();
|
BearerTokenError error = (BearerTokenError) ex.getError();
|
||||||
assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
|
assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
|
||||||
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||||
});
|
});
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {
|
public void resolveWhenLowercaseHeaderIsPresentThenTokenIsResolved() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
// @formatter:off
|
||||||
"bearer " + TEST_TOKEN);
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "bearer " + TEST_TOKEN);
|
||||||
|
// @formatter:on
|
||||||
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,48 +114,65 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenHeaderWithWrongSchemeIsPresentThenTokenIsNotResolved() {
|
public void resolveWhenHeaderWithWrongSchemeIsPresentThenTokenIsNotResolved() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
// @formatter:off
|
||||||
"Basic " + Base64.getEncoder().encodeToString("test:test".getBytes()));
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.header(HttpHeaders.AUTHORIZATION,
|
||||||
|
"Basic " + Base64.getEncoder().encodeToString("test:test".getBytes()));
|
||||||
|
// @formatter:on
|
||||||
assertThat(convertToToken(request)).isNull();
|
assertThat(convertToToken(request)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenHeaderWithMissingTokenIsPresentThenAuthenticationExceptionIsThrown() {
|
public void resolveWhenHeaderWithMissingTokenIsPresentThenAuthenticationExceptionIsThrown() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
// @formatter:off
|
||||||
"Bearer ");
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))
|
.header(HttpHeaders.AUTHORIZATION, "Bearer ");
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
|
.isThrownBy(() -> convertToToken(request))
|
||||||
.withMessageContaining(("Bearer token is malformed"));
|
.withMessageContaining(("Bearer token is malformed"));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenHeaderWithInvalidCharactersIsPresentThenAuthenticationExceptionIsThrown() {
|
public void resolveWhenHeaderWithInvalidCharactersIsPresentThenAuthenticationExceptionIsThrown() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
// @formatter:off
|
||||||
"Bearer an\"invalid\"token");
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))
|
.header(HttpHeaders.AUTHORIZATION, "Bearer an\"invalid\"token");
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
|
.isThrownBy(() -> convertToToken(request))
|
||||||
.withMessageContaining(("Bearer token is malformed"));
|
.withMessageContaining(("Bearer token is malformed"));
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
// gh-8865
|
// gh-8865
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenHeaderWithInvalidCharactersIsPresentAndNotSubscribedThenNoneExceptionIsThrown() {
|
public void resolveWhenHeaderWithInvalidCharactersIsPresentAndNotSubscribedThenNoneExceptionIsThrown() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").header(HttpHeaders.AUTHORIZATION,
|
// @formatter:off
|
||||||
"Bearer an\"invalid\"token");
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "Bearer an\"invalid\"token");
|
||||||
|
// @formatter:on
|
||||||
this.converter.convert(MockServerWebExchange.from(request));
|
this.converter.convert(MockServerWebExchange.from(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenValidHeaderIsPresentTogetherWithQueryParameterThenAuthenticationExceptionIsThrown() {
|
public void resolveWhenValidHeaderIsPresentTogetherWithQueryParameterThenAuthenticationExceptionIsThrown() {
|
||||||
|
// @formatter:off
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
.queryParam("access_token", TEST_TOKEN).header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_TOKEN);
|
.queryParam("access_token", TEST_TOKEN)
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + TEST_TOKEN);
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
|
.isThrownBy(() -> convertToToken(request))
|
||||||
.withMessageContaining("Found multiple bearer tokens in the request");
|
.withMessageContaining("Found multiple bearer tokens in the request");
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenQueryParameterIsPresentAndSupportedThenTokenIsResolved() {
|
public void resolveWhenQueryParameterIsPresentAndSupportedThenTokenIsResolved() {
|
||||||
this.converter.setAllowUriQueryParameter(true);
|
this.converter.setAllowUriQueryParameter(true);
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").queryParam("access_token",
|
// @formatter:off
|
||||||
TEST_TOKEN);
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.queryParam("access_token", TEST_TOKEN);
|
||||||
|
// @formatter:on
|
||||||
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
assertThat(convertToToken(request).getToken()).isEqualTo(TEST_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,20 +180,26 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenQueryParameterIsEmptyAndSupportedThenOAuth2AuthenticationException() {
|
public void resolveWhenQueryParameterIsEmptyAndSupportedThenOAuth2AuthenticationException() {
|
||||||
this.converter.setAllowUriQueryParameter(true);
|
this.converter.setAllowUriQueryParameter(true);
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").queryParam("access_token", "");
|
// @formatter:off
|
||||||
assertThatExceptionOfType(OAuth2AuthenticationException.class).isThrownBy(() -> convertToToken(request))
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.queryParam("access_token", "");
|
||||||
|
assertThatExceptionOfType(OAuth2AuthenticationException.class)
|
||||||
|
.isThrownBy(() -> convertToToken(request))
|
||||||
.satisfies((ex) -> {
|
.satisfies((ex) -> {
|
||||||
BearerTokenError error = (BearerTokenError) ex.getError();
|
BearerTokenError error = (BearerTokenError) ex.getError();
|
||||||
assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
|
assertThat(error.getErrorCode()).isEqualTo(BearerTokenErrorCodes.INVALID_TOKEN);
|
||||||
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
assertThat(error.getUri()).isEqualTo("https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||||
assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
assertThat(error.getHttpStatus()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||||
});
|
});
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {
|
public void resolveWhenQueryParameterIsPresentAndNotSupportedThenTokenIsNotResolved() {
|
||||||
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/").queryParam("access_token",
|
// @formatter:off
|
||||||
TEST_TOKEN);
|
MockServerHttpRequest.BaseBuilder<?> request = MockServerHttpRequest.get("/")
|
||||||
|
.queryParam("access_token", TEST_TOKEN);
|
||||||
|
// @formatter:on
|
||||||
assertThat(convertToToken(request)).isNull();
|
assertThat(convertToToken(request)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +209,11 @@ public class ServerBearerTokenAuthenticationConverterTests {
|
||||||
|
|
||||||
private BearerTokenAuthenticationToken convertToToken(MockServerHttpRequest request) {
|
private BearerTokenAuthenticationToken convertToToken(MockServerHttpRequest request) {
|
||||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||||
return this.converter.convert(exchange).cast(BearerTokenAuthenticationToken.class).block();
|
// @formatter:off
|
||||||
|
return this.converter.convert(exchange)
|
||||||
|
.cast(BearerTokenAuthenticationToken.class)
|
||||||
|
.block();
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue