private static String resolveFromRequestParameters(HttpServletRequest request) { String[] values = request.getParameterValues("access_token"); if (values == null || values.length == 0) { return null; } if (values.length == 1) { return values[0]; } BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, "Found multiple bearer tokens in the request", "https://tools.ietf.org/html/rfc6750#section-3.1"); throw new OAuth2AuthenticationException(error); }
private static String resolveFromAuthorizationHeader(HttpServletRequest request) { String authorization = request.getHeader(HttpHeaders.AUTHORIZATION); if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) { Matcher matcher = authorizationPattern.matcher(authorization); if (!matcher.matches()) { BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED, "Bearer token is malformed", "https://tools.ietf.org/html/rfc6750#section-3.1"); throw new OAuth2AuthenticationException(error); } return matcher.group("token"); } return null; }
private static String resolveFromAuthorizationHeader(HttpHeaders headers) { String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION); if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) { Matcher matcher = authorizationPattern.matcher(authorization); if ( !matcher.matches() ) { BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.BAD_REQUEST, "Bearer token is malformed", "https://tools.ietf.org/html/rfc6750#section-3.1"); throw new OAuth2AuthenticationException(error); } return matcher.group("token"); } return null; }
private OAuth2AuthenticationException onError(JwtException e) { OAuth2Error invalidRequest = invalidToken(e.getMessage()); return new OAuth2AuthenticationException(invalidRequest, e.getMessage()); }
private Mono<OidcUserInfo> getUserInfo(OidcUserRequest userRequest) { if (!OidcUserRequestUtils.shouldRetrieveUserInfo(userRequest)) { return Mono.empty(); } return this.oauth2UserService.loadUser(userRequest) .map(OAuth2User::getAttributes) .map(OidcUserInfo::new) .doOnNext(userInfo -> { String subject = userInfo.getSubject(); if (subject == null || !subject.equals(userRequest.getIdToken().getSubject())) { OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } }); }
@Test public void doFilterWhenAuthenticationFailsThenPropagatesError() throws ServletException, IOException { BearerTokenError error = new BearerTokenError( BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED, "description", "uri" ); OAuth2AuthenticationException exception = new OAuth2AuthenticationException(error); when(this.bearerTokenResolver.resolve(this.request)).thenReturn("token"); when(this.authenticationManager.authenticate(any(BearerTokenAuthenticationToken.class))) .thenThrow(exception); this.filter.doFilter(this.request, this.response, this.filterChain); verify(this.authenticationEntryPoint).commence(this.request, this.response, exception); }
@Test public void doFilterWhenMalformedBearerTokenThenPropagatesError() throws ServletException, IOException { BearerTokenError error = new BearerTokenError( BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, "description", "uri"); OAuth2AuthenticationException exception = new OAuth2AuthenticationException(error); when(this.bearerTokenResolver.resolve(this.request)).thenThrow(exception); dontAuthenticate(); verify(this.authenticationEntryPoint).commence(this.request, this.response, exception); }
@Test public void commenceWhenOAuth2AuthenticationExceptionThenContainsErrorInformation() { OAuth2Error oauthError = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST); OAuth2AuthenticationException exception = new OAuth2AuthenticationException(oauthError); this.entryPoint.commence(this.exchange, exception).block(); assertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo("Bearer error=\"invalid_request\""); assertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); }
@Test public void commenceWhenOAuth2ErrorCompleteThenContainsErrorInformation() { OAuth2Error oauthError = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "Oops", "https://example.com"); OAuth2AuthenticationException exception = new OAuth2AuthenticationException(oauthError); this.entryPoint.commence(this.exchange, exception).block(); assertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo("Bearer error=\"invalid_request\", error_description=\"Oops\", error_uri=\"https://example.com\""); assertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); }
@Test public void commenceWhenInsufficientScopeErrorThenStatus403AndHeaderWithError() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN, null, null); this.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error)); assertThat(response.getStatus()).isEqualTo(403); assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"insufficient_scope\""); }
@Test public void commenceWhenBearerTokenThenErrorInformation() { OAuth2Error oauthError = new BearerTokenError(OAuth2ErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, "Oops", "https://example.com"); OAuth2AuthenticationException exception = new OAuth2AuthenticationException(oauthError); this.entryPoint.commence(this.exchange, exception).block(); assertThat(getResponse().getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE)).isEqualTo("Bearer error=\"invalid_request\", error_description=\"Oops\", error_uri=\"https://example.com\""); assertThat(getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); }
@Test public void commenceWhenInvalidTokenErrorThenStatus401AndHeaderWithError() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED, null, null); this.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error)); assertThat(response.getStatus()).isEqualTo(401); assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"invalid_token\""); }
@Test public void commenceWhenInvalidRequestErrorThenStatus400AndHeaderWithErrorUri() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, null, "http://example.com", null); this.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error)); assertThat(response.getStatus()).isEqualTo(400); assertThat(response.getHeader("WWW-Authenticate")) .isEqualTo("Bearer error=\"invalid_request\", error_uri=\"http://example.com\""); }
private String token(ServerHttpRequest request) { String authorizationHeaderToken = resolveFromAuthorizationHeader(request.getHeaders()); String parameterToken = request.getQueryParams().getFirst("access_token"); if (authorizationHeaderToken != null) { if (parameterToken != null) { BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, "Found multiple bearer tokens in the request", "https://tools.ietf.org/html/rfc6750#section-3.1"); throw new OAuth2AuthenticationException(error); } return authorizationHeaderToken; } else if (parameterToken != null && isParameterTokenSupportedForRequest(request)) { return parameterToken; } return null; }
@Test public void commenceWhenInvalidRequestErrorThenStatus400AndHeaderWithErrorDetails() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, "The access token expired", null, null); this.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error)); assertThat(response.getStatus()).isEqualTo(400); assertThat(response.getHeader("WWW-Authenticate")) .isEqualTo("Bearer error=\"invalid_request\", error_description=\"The access token expired\""); }
@Test public void commenceWhenInsufficientScopeErrorThenStatus403AndHeaderWithErrorAndScope() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN, null, null, "test.read test.write"); this.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error)); assertThat(response.getStatus()).isEqualTo(403); assertThat(response.getHeader("WWW-Authenticate")) .isEqualTo("Bearer error=\"insufficient_scope\", scope=\"test.read test.write\""); }
@Test public void commenceWhenInvalidRequestErrorThenStatus400AndHeaderWithError() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); BearerTokenError error = new BearerTokenError( BearerTokenErrorCodes.INVALID_REQUEST, HttpStatus.BAD_REQUEST, null, null); this.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error)); assertThat(response.getStatus()).isEqualTo(400); assertThat(response.getHeader("WWW-Authenticate")).isEqualTo("Bearer error=\"invalid_request\""); }
private OidcIdToken createOidcToken(ClientRegistration clientRegistration, OAuth2AccessTokenResponse accessTokenResponse) { JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(clientRegistration); Jwt jwt; try { jwt = jwtDecoder.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN)); } catch (JwtException ex) { OAuth2Error invalidIdTokenError = new OAuth2Error(INVALID_ID_TOKEN_ERROR_CODE, ex.getMessage(), null); throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString(), ex); } OidcIdToken idToken = new OidcIdToken(jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaims()); return idToken; } }
@Test public void commenceWhenInsufficientScopeAndRealmSetThenStatus403AndHeaderWithErrorAndAllDetails() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); BearerTokenError error = new BearerTokenError(BearerTokenErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN, "Insufficient scope", "http://example.com", "test.read test.write"); this.authenticationEntryPoint.setRealmName("test"); this.authenticationEntryPoint.commence(request, response, new OAuth2AuthenticationException(error)); assertThat(response.getStatus()).isEqualTo(403); assertThat(response.getHeader("WWW-Authenticate")).isEqualTo( "Bearer realm=\"test\", error=\"insufficient_scope\", error_description=\"Insufficient scope\", " + "error_uri=\"http://example.com\", scope=\"test.read test.write\""); }
@Test public void getWhenUsingCustomAuthenticationManagerThenUsesItAccordingly() { this.spring.register(CustomAuthenticationManagerConfig.class).autowire(); ReactiveAuthenticationManager authenticationManager = this.spring.getContext().getBean( ReactiveAuthenticationManager.class); when(authenticationManager.authenticate(any(Authentication.class))) .thenReturn(Mono.error(new OAuth2AuthenticationException(new OAuth2Error("mock-failure")))); this.client.get() .headers(headers -> headers.setBearerAuth(this.messageReadToken)) .exchange() .expectStatus().isUnauthorized() .expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith("Bearer error=\"mock-failure\"")); }