@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { OAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication; // Section 3.1.2.1 Authentication Request - http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest // scope // REQUIRED. OpenID Connect requests MUST contain the "openid" scope value. if (authorizationCodeAuthentication.getAuthorizationExchange() .getAuthorizationRequest().getScopes().contains(OidcScopes.OPENID)) { OAuth2Error oauth2Error = new OAuth2Error( "oidc_provider_not_configured", "An OpenID Connect Authentication Provider has not been configured. " + "Check to ensure you include the dependency 'spring-security-oauth2-jose'.", null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } return null; }
@Override public Map<String, String> convert(OAuth2Error oauth2Error) { Map<String, String> parameters = new HashMap<>(); parameters.put(OAuth2ParameterNames.ERROR, oauth2Error.getErrorCode()); if (StringUtils.hasText(oauth2Error.getDescription())) { parameters.put(OAuth2ParameterNames.ERROR_DESCRIPTION, oauth2Error.getDescription()); } if (StringUtils.hasText(oauth2Error.getUri())) { parameters.put(OAuth2ParameterNames.ERROR_URI, oauth2Error.getUri()); } return parameters; } }
@Override public String toString() { return "[" + this.getErrorCode() + "] " + (this.getDescription() != null ? this.getDescription() : ""); } }
@Test public void constructorWhenAllParametersProvidedAndValidThenCreated() { OAuth2Error error = new OAuth2Error(ERROR_CODE, ERROR_DESCRIPTION, ERROR_URI); assertThat(error.getErrorCode()).isEqualTo(ERROR_CODE); assertThat(error.getDescription()).isEqualTo(ERROR_DESCRIPTION); assertThat(error.getUri()).isEqualTo(ERROR_URI); } }
/** * Constructs a {@code ClientAuthorizationRequiredException} using the provided parameters. * * @param clientRegistrationId the identifier for the client's registration */ public ClientAuthorizationRequiredException(String clientRegistrationId) { super(new OAuth2Error(CLIENT_AUTHORIZATION_REQUIRED_ERROR_CODE, "Authorization required for Client Registration Id: " + clientRegistrationId, null)); Assert.hasText(clientRegistrationId, "clientRegistrationId cannot be empty"); this.clientRegistrationId = clientRegistrationId; }
OAuth2Error oauth2Error = new OAuth2Error( MISSING_USER_INFO_URI_ERROR_CODE, "Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: " + null ); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); OAuth2Error oauth2Error = new OAuth2Error( MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE, "Missing required \"user name\" attribute name in UserInfoEndpoint for Client Registration: " + null ); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); errorDetails.append("UserInfo Uri: ").append( userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); errorDetails.append(", Error Code: ").append(oauth2Error.getErrorCode()); if (oauth2Error.getDescription() != null) { errorDetails.append(", Error Description: ").append(oauth2Error.getDescription()); oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, "An error occurred while attempting to retrieve the UserInfo Resource: " + errorDetails.toString(), null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); } catch (RestClientException ex) { OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, "An error occurred while attempting to retrieve the UserInfo Resource: " + ex.getMessage(), null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
/** * Constructs an {@code OAuth2AuthenticationException} using the provided parameters. * * @param error the {@link OAuth2Error OAuth 2.0 Error} */ public OAuth2AuthenticationException(OAuth2Error error) { this(error, error.getDescription()); }
/** * Returns {@code true} if the Authorization Request failed, otherwise {@code false}. * * @return {@code true} if the Authorization Request failed, otherwise {@code false} */ public boolean statusError() { return (this.error != null && this.error.getErrorCode() != null); }
/** * Constructs an {@code OAuth2AuthorizationException} using the provided parameters. * * @param error the {@link OAuth2Error OAuth 2.0 Error} * @param cause the root cause */ public OAuth2AuthorizationException(OAuth2Error error, Throwable cause) { super(error.toString(), cause); this.error = error; }
@Override public OAuth2Error convert(Map<String, String> parameters) { String errorCode = parameters.get(OAuth2ParameterNames.ERROR); String errorDescription = parameters.get(OAuth2ParameterNames.ERROR_DESCRIPTION); String errorUri = parameters.get(OAuth2ParameterNames.ERROR_URI); return new OAuth2Error(errorCode, errorDescription, errorUri); } }
OAuth2Error oauth2Error = new OAuth2Error( MISSING_USER_INFO_URI_ERROR_CODE, "Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: " + null ); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); OAuth2Error oauth2Error = new OAuth2Error( MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE, "Missing required \"user name\" attribute name in UserInfoEndpoint for Client Registration: " + null ); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); errorDetails.append("UserInfo Uri: ").append( userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri()); errorDetails.append(", Error Code: ").append(oauth2Error.getErrorCode()); if (oauth2Error.getDescription() != null) { errorDetails.append(", Error Description: ").append(oauth2Error.getDescription()); oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, "An error occurred while attempting to retrieve the UserInfo Resource: " + errorDetails.toString(), null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex); } catch (RestClientException ex) { OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE, "An error occurred while attempting to retrieve the UserInfo Resource: " + ex.getMessage(), null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
@Override public String toString() { return "[" + this.getErrorCode() + "] " + (this.getDescription() != null ? this.getDescription() : ""); } }
private Jwt validateJwt(Jwt jwt){ OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt); if (result.hasErrors()) { String description = result.getErrors().iterator().next().getDescription(); throw new JwtValidationException( String.format(DECODING_ERROR_MESSAGE_TEMPLATE, description), result.getErrors()); } return jwt; } }
private Predicate<? super Throwable> errorCode(String errorCode) { return failed -> ((OAuth2AuthenticationException) failed).getError().getErrorCode() == errorCode; } }
/** * Constructs an {@code OAuth2AuthorizationException} using the provided parameters. * * @param error the {@link OAuth2Error OAuth 2.0 Error} */ public OAuth2AuthorizationException(OAuth2Error error) { super(error.toString()); this.error = error; }
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()); } }); }
private Map<String, String> createParameters(AuthenticationException authException) { Map<String, String> parameters = new LinkedHashMap<>(); if (this.realmName != null) { parameters.put("realm", this.realmName); } if (authException instanceof OAuth2AuthenticationException) { OAuth2Error error = ((OAuth2AuthenticationException) authException).getError(); parameters.put("error", error.getErrorCode()); if (StringUtils.hasText(error.getDescription())) { parameters.put("error_description", error.getDescription()); } if (StringUtils.hasText(error.getUri())) { parameters.put("error_uri", error.getUri()); } if (error instanceof BearerTokenError) { BearerTokenError bearerTokenError = (BearerTokenError) error; if (StringUtils.hasText(bearerTokenError.getScope())) { parameters.put("scope", bearerTokenError.getScope()); } } } return parameters; }
private static OAuth2Error invalidIdToken(Map<String, Object> invalidClaims) { String claimsDetail = invalidClaims.entrySet().stream() .map(it -> it.getKey() + " (" + it.getValue() + ")") .collect(Collectors.joining(", ")); return new OAuth2Error("invalid_id_token", "The ID Token contains invalid claims: " + claimsDetail, "https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation"); }
@Override public String toString() { return "[" + this.getErrorCode() + "] " + (this.getDescription() != null ? this.getDescription() : ""); } }
private Jwt validateJwt(Jwt jwt) { OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt); if ( result.hasErrors() ) { String message = result.getErrors().iterator().next().getDescription(); throw new JwtValidationException(message, result.getErrors()); } return jwt; }