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