diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java index c25971c760..8fa758ad05 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java @@ -18,12 +18,16 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.cl import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.context.ApplicationContext; import org.springframework.core.ResolvableType; +import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider; +import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken; import org.springframework.security.oauth2.client.endpoint.NimbusAuthorizationCodeTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; @@ -40,13 +44,17 @@ import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter; import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.oidc.OidcScopes; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import java.util.ArrayList; import java.util.HashMap; @@ -246,19 +254,25 @@ public final class OAuth2LoginConfigurer> exten } http.authenticationProvider(this.postProcess(oauth2LoginAuthenticationProvider)); - OAuth2UserService oidcUserService = this.userInfoEndpointConfig.oidcUserService; - if (oidcUserService == null) { - oidcUserService = new OidcUserService(); - } + boolean oidcAuthenticationProviderEnabled = ClassUtils.isPresent( + "org.springframework.security.oauth2.jwt.JwtDecoder", this.getClass().getClassLoader()); - OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider = - new OidcAuthorizationCodeAuthenticationProvider( - accessTokenResponseClient, oidcUserService); - if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) { - oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper( - this.userInfoEndpointConfig.userAuthoritiesMapper); + if (oidcAuthenticationProviderEnabled) { + OAuth2UserService oidcUserService = this.userInfoEndpointConfig.oidcUserService; + if (oidcUserService == null) { + oidcUserService = new OidcUserService(); + } + + OidcAuthorizationCodeAuthenticationProvider oidcAuthorizationCodeAuthenticationProvider = + new OidcAuthorizationCodeAuthenticationProvider(accessTokenResponseClient, oidcUserService); + if (this.userInfoEndpointConfig.userAuthoritiesMapper != null) { + oidcAuthorizationCodeAuthenticationProvider.setAuthoritiesMapper( + this.userInfoEndpointConfig.userAuthoritiesMapper); + } + http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider)); + } else { + http.authenticationProvider(new OidcAuthenticationRequestChecker()); } - http.authenticationProvider(this.postProcess(oidcAuthorizationCodeAuthenticationProvider)); this.initDefaultLoginFilter(http); } @@ -359,4 +373,35 @@ public final class OAuth2LoginConfigurer> exten loginPageGeneratingFilter.setLoginPageUrl(this.getLoginPage()); loginPageGeneratingFilter.setFailureUrl(this.getFailureUrl()); } + + private static class OidcAuthenticationRequestChecker implements AuthenticationProvider { + + @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 boolean supports(Class authentication) { + return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication); + } + } + }