From 0848c7dbf5bf28f60ad98fa5dbea2a91b202c328 Mon Sep 17 00:00:00 2001 From: Greg Li Date: Mon, 8 Jan 2024 14:23:49 +0800 Subject: [PATCH 1/3] gh-1454: Disable device authorization grant by default. Such as, deviceAuthorizationEndpoint and deviceVerificationEndpoint --- docs/src/test/java/sample/jpa/JpaTests.java | 9 ++- .../OAuth2AuthorizationServerConfigurer.java | 65 +++++++++++++++---- ...orizationServerMetadataEndpointFilter.java | 2 +- .../OAuth2DeviceCodeGrantTests.java | 59 ++++++++++++++--- 4 files changed, 110 insertions(+), 25 deletions(-) diff --git a/docs/src/test/java/sample/jpa/JpaTests.java b/docs/src/test/java/sample/jpa/JpaTests.java index 8720383dd..cbad56d2b 100644 --- a/docs/src/test/java/sample/jpa/JpaTests.java +++ b/docs/src/test/java/sample/jpa/JpaTests.java @@ -69,6 +69,7 @@ * Tests for the guide How-to: Implement core services with JPA. * * @author Steve Riesenberg + * @author Greg Li */ @ExtendWith(SpringTestContextExtension.class) public class JpaTests { @@ -88,7 +89,7 @@ public class JpaTests { @Test public void oidcLoginWhenJpaCoreServicesAutowiredThenUsed() throws Exception { - this.spring.register(AuthorizationServerConfig.class).autowire(); + this.spring.register(AuthorizationServerConfigDeviceAuthorize.class).autowire(); assertThat(this.registeredClientRepository).isInstanceOf(JpaRegisteredClientRepository.class); assertThat(this.authorizationService).isInstanceOf(JpaOAuth2AuthorizationService.class); assertThat(this.authorizationConsentService).isInstanceOf(JpaOAuth2AuthorizationConsentService.class); @@ -135,7 +136,7 @@ public void oidcLoginWhenJpaCoreServicesAutowiredThenUsed() throws Exception { @Test public void deviceAuthorizationWhenJpaCoreServicesAutowiredThenSuccess() throws Exception { - this.spring.register(AuthorizationServerConfig.class).autowire(); + this.spring.register(AuthorizationServerConfigDeviceAuthorize.class).autowire(); assertThat(this.registeredClientRepository).isInstanceOf(JpaRegisteredClientRepository.class); assertThat(this.authorizationService).isInstanceOf(JpaOAuth2AuthorizationService.class); assertThat(this.authorizationConsentService).isInstanceOf(JpaOAuth2AuthorizationConsentService.class); @@ -191,13 +192,15 @@ private OAuth2Authorization findAuthorization(String token, String tokenType) { @EnableWebSecurity @EnableAutoConfiguration @ComponentScan - static class AuthorizationServerConfig { + static class AuthorizationServerConfigDeviceAuthorize { @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) + .deviceAuthorizationEndpoint(Customizer.withDefaults()) + .deviceVerificationEndpoint(Customizer.withDefaults()) .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 // @formatter:off diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java index 1b04ac158..64596f6b2 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java @@ -45,6 +45,8 @@ import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext; +import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder; import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter; @@ -55,6 +57,7 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; +import org.springframework.web.util.UriComponentsBuilder; /** * An {@link AbstractHttpConfigurer} for OAuth 2.0 Authorization Server support. @@ -64,6 +67,7 @@ * @author Gerardo Roza * @author Ovidiu Popa * @author Gaurav Tiwari + * @author Greg Li * @since 0.0.1 * @see AbstractHttpConfigurer * @see OAuth2ClientAuthenticationConfigurer @@ -218,26 +222,40 @@ public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(Customizer deviceAuthorizationEndpointCustomizer) { - deviceAuthorizationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class)); + OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = + getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class); + if (deviceAuthorizationEndpointConfigurer == null) { + addConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class, + new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess)); + deviceAuthorizationEndpointConfigurer = getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class); + } + deviceAuthorizationEndpointCustomizer.customize(deviceAuthorizationEndpointConfigurer); return this; } /** - * Configures the OAuth 2.0 Device Verification Endpoint. + * Configures the OAuth 2.0 Device Verification Endpoint (disabled by default). * * @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceVerificationEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 1.1 */ public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(Customizer deviceVerificationEndpointCustomizer) { - deviceVerificationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class)); + OAuth2DeviceVerificationEndpointConfigurer deviceVerificationEndpointConfigurer = + getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class); + if (deviceVerificationEndpointConfigurer == null) { + addConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class, + new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); + deviceVerificationEndpointConfigurer = getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class); + } + deviceVerificationEndpointCustomizer.customize(deviceVerificationEndpointConfigurer); return this; } @@ -319,19 +337,45 @@ public void init(HttpSecurity httpSecurity) { ExceptionHandlingConfigurer exceptionHandling = httpSecurity.getConfigurer(ExceptionHandlingConfigurer.class); if (exceptionHandling != null) { + OrRequestMatcher preferredRequestMatcher = null; + if (getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class) != null) { + preferredRequestMatcher = new OrRequestMatcher( + getRequestMatcher(OAuth2TokenEndpointConfigurer.class), + getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class), + getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class), + getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class)); + } else { + preferredRequestMatcher = new OrRequestMatcher( + getRequestMatcher(OAuth2TokenEndpointConfigurer.class), + getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class), + getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class)); + } exceptionHandling.defaultAuthenticationEntryPointFor( new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), - new OrRequestMatcher( - getRequestMatcher(OAuth2TokenEndpointConfigurer.class), - getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class), - getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class), - getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class)) + preferredRequestMatcher ); } } @Override public void configure(HttpSecurity httpSecurity) { + OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = + getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class); + if (deviceAuthorizationEndpointConfigurer != null) { + OAuth2AuthorizationServerMetadataEndpointConfigurer auth2AuthorizationServerMetadataEndpointConfigurer = + getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class); + + auth2AuthorizationServerMetadataEndpointConfigurer + .addDefaultAuthorizationServerMetadataCustomizer((builder) -> { + AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext(); + String issuer = authorizationServerContext.getIssuer(); + AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings(); + String deviceAuthorizationEndpoint = UriComponentsBuilder.fromUriString(issuer) + .path(authorizationServerSettings.getDeviceAuthorizationEndpoint()).build().toUriString(); + + builder.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint); + }); + } this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity)); AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); @@ -359,8 +403,7 @@ private Map, AbstractOAuth2Configurer> configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess)); configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess)); configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess)); - configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class, new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess)); - configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); + //configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); return configurers; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java index c561260c0..39da328af 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java @@ -46,6 +46,7 @@ * * @author Daniel Garnier-Moiroux * @author Joe Grandja + * @author Greg Li * @since 0.1.1 * @see OAuth2AuthorizationServerMetadata * @see AuthorizationServerSettings @@ -92,7 +93,6 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse OAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder() .issuer(issuer) .authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint())) - .deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint())) .tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint())) .tokenEndpointAuthenticationMethods(clientAuthenticationMethods()) .jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint())) diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java index f8b039833..ccec19d29 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java @@ -33,6 +33,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -45,6 +47,8 @@ import org.springframework.mock.http.client.MockClientHttpResponse; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -72,6 +76,7 @@ import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; import org.springframework.security.oauth2.server.authorization.test.SpringTestContext; import org.springframework.security.oauth2.server.authorization.test.SpringTestContextExtension; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.LinkedMultiValueMap; @@ -90,6 +95,7 @@ * Integration tests for OAuth 2.0 Device Grant. * * @author Steve Riesenberg + * @author Greg Li */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2DeviceCodeGrantTests { @@ -158,7 +164,7 @@ public static void destroy() { @Test public void requestWhenDeviceAuthorizationRequestNotAuthenticatedThenUnauthorized() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -179,9 +185,32 @@ public void requestWhenDeviceAuthorizationRequestNotAuthenticatedThenUnauthorize // @formatter:on } + @Test + public void requestWhenDeviceAuthorizationRequestDisabledThenUnauthorized() throws Exception { + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); + + // @formatter:off + RegisteredClient registeredClient = TestRegisteredClients.registeredClient() + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); + // @formatter:on + this.registeredClientRepository.save(registeredClient); + + MultiValueMap parameters = new LinkedMultiValueMap<>(); + parameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); + parameters.set(OAuth2ParameterNames.SCOPE, + StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " ")); + + // @formatter:off + this.mvc.perform(post(DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI) + .params(parameters)) + .andExpect(status().isUnauthorized()); + // @formatter:on + } + @Test public void requestWhenRegisteredClientMissingThenUnauthorized() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -204,7 +233,7 @@ public void requestWhenRegisteredClientMissingThenUnauthorized() throws Exceptio @Test public void requestWhenDeviceAuthorizationRequestValidThenReturnDeviceAuthorizationResponse() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -252,7 +281,7 @@ public void requestWhenDeviceAuthorizationRequestValidThenReturnDeviceAuthorizat @Test public void requestWhenDeviceVerificationRequestUnauthenticatedThenUnauthorized() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -286,7 +315,7 @@ public void requestWhenDeviceVerificationRequestUnauthenticatedThenUnauthorized( @Test public void requestWhenDeviceVerificationRequestValidThenDisplaysConsentPage() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -335,7 +364,7 @@ public void requestWhenDeviceVerificationRequestValidThenDisplaysConsentPage() t @Test public void requestWhenDeviceAuthorizationConsentRequestUnauthenticatedThenBadRequest() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -373,7 +402,7 @@ public void requestWhenDeviceAuthorizationConsentRequestUnauthenticatedThenBadRe @Test public void requestWhenDeviceAuthorizationConsentRequestValidThenRedirectsToSuccessPage() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -423,7 +452,7 @@ public void requestWhenDeviceAuthorizationConsentRequestValidThenRedirectsToSucc @Test public void requestWhenAccessTokenRequestUnauthenticatedThenUnauthorized() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -459,7 +488,7 @@ public void requestWhenAccessTokenRequestUnauthenticatedThenUnauthorized() throw @Test public void requestWhenAccessTokenRequestValidThenReturnAccessTokenResponse() throws Exception { - this.spring.register(AuthorizationServerConfiguration.class).autowire(); + this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire(); // @formatter:off RegisteredClient registeredClient = TestRegisteredClients.registeredClient() @@ -545,7 +574,17 @@ private static Function, Boolea @EnableWebSecurity @Import(OAuth2AuthorizationServerConfiguration.class) - static class AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationDeviceAuthorize { + + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { + OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); + http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) + .deviceAuthorizationEndpoint(Customizer.withDefaults()) // Enable deviceAuthorizationEndpoint + .deviceVerificationEndpoint(Customizer.withDefaults()); // Enable deviceVerificationEndpoint + return http.build(); + } @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { From d33e4d2f2586ac8c66328cda3758001a3d831cfd Mon Sep 17 00:00:00 2001 From: Greg Li Date: Mon, 8 Jan 2024 14:32:45 +0800 Subject: [PATCH 2/3] gh-1454: Disable device authorization grant by default. Such as, deviceAuthorizationEndpoint and deviceVerificationEndpoint --- .../web/configurers/OAuth2AuthorizationServerConfigurer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java index 64596f6b2..49338edee 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java @@ -403,7 +403,6 @@ private Map, AbstractOAuth2Configurer> configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess)); configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess)); configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess)); - //configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); return configurers; } From e32436924acc213f13b0cdd8ddf5e9d936b36cf5 Mon Sep 17 00:00:00 2001 From: Greg Li Date: Mon, 15 Jan 2024 15:12:26 +0800 Subject: [PATCH 3/3] gh-1454: Provide a configuration "enableDeviceAuthorizationEndpoint" to support enable/disable device authorization grant. The default value of enableDeviceAuthorizationEndpoint is true for backward capability. --- .../OAuth2AuthorizationServerConfigurer.java | 39 ++++++++----------- ...DeviceAuthorizationEndpointConfigurer.java | 11 ++++++ .../config/AuthorizationServerConfig.java | 6 ++- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java index 49338edee..65e656ca3 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java @@ -222,40 +222,26 @@ public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(Customizer deviceAuthorizationEndpointCustomizer) { - OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = - getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class); - if (deviceAuthorizationEndpointConfigurer == null) { - addConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class, - new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess)); - deviceAuthorizationEndpointConfigurer = getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class); - } - deviceAuthorizationEndpointCustomizer.customize(deviceAuthorizationEndpointConfigurer); + deviceAuthorizationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class)); return this; } /** - * Configures the OAuth 2.0 Device Verification Endpoint (disabled by default). + * Configures the OAuth 2.0 Device Verification Endpoint. * * @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceVerificationEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 1.1 */ public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(Customizer deviceVerificationEndpointCustomizer) { - OAuth2DeviceVerificationEndpointConfigurer deviceVerificationEndpointConfigurer = - getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class); - if (deviceVerificationEndpointConfigurer == null) { - addConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class, - new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); - deviceVerificationEndpointConfigurer = getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class); - } - deviceVerificationEndpointCustomizer.customize(deviceVerificationEndpointConfigurer); + deviceVerificationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class)); return this; } @@ -325,6 +311,10 @@ public void init(HttpSecurity httpSecurity) { } }); } + if (!isDeviceAuthorizationEnabled()) { + this.configurers.remove(OAuth2DeviceAuthorizationEndpointConfigurer.class); + this.configurers.remove(OAuth2DeviceVerificationEndpointConfigurer.class); + } List requestMatchers = new ArrayList<>(); this.configurers.values().forEach(configurer -> { @@ -338,7 +328,7 @@ public void init(HttpSecurity httpSecurity) { ExceptionHandlingConfigurer exceptionHandling = httpSecurity.getConfigurer(ExceptionHandlingConfigurer.class); if (exceptionHandling != null) { OrRequestMatcher preferredRequestMatcher = null; - if (getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class) != null) { + if (isDeviceAuthorizationEnabled()) { preferredRequestMatcher = new OrRequestMatcher( getRequestMatcher(OAuth2TokenEndpointConfigurer.class), getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class), @@ -359,9 +349,7 @@ public void init(HttpSecurity httpSecurity) { @Override public void configure(HttpSecurity httpSecurity) { - OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = - getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class); - if (deviceAuthorizationEndpointConfigurer != null) { + if (isDeviceAuthorizationEnabled()) { OAuth2AuthorizationServerMetadataEndpointConfigurer auth2AuthorizationServerMetadataEndpointConfigurer = getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class); @@ -395,6 +383,11 @@ private boolean isOidcEnabled() { return getConfigurer(OidcConfigurer.class) != null; } + private boolean isDeviceAuthorizationEnabled() { + OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer = getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class); + return deviceAuthorizationEndpointConfigurer != null && deviceAuthorizationEndpointConfigurer.isEnableDeviceAuthorizationEndpoint(); + } + private Map, AbstractOAuth2Configurer> createConfigurers() { Map, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>(); configurers.put(OAuth2ClientAuthenticationConfigurer.class, new OAuth2ClientAuthenticationConfigurer(this::postProcess)); @@ -403,6 +396,8 @@ private Map, AbstractOAuth2Configurer> configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess)); configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess)); configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class, new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); return configurers; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java index 765548f32..cfe11a09c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java @@ -62,7 +62,13 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; private AuthenticationSuccessHandler deviceAuthorizationResponseHandler; private AuthenticationFailureHandler errorResponseHandler; + + public boolean isEnableDeviceAuthorizationEndpoint() { + return enableDeviceAuthorizationEndpoint; + } + private String verificationUri; + private boolean enableDeviceAuthorizationEndpoint = true; /** * Restrict for internal use only. @@ -161,6 +167,11 @@ public OAuth2DeviceAuthorizationEndpointConfigurer verificationUri(String verifi return this; } + public OAuth2DeviceAuthorizationEndpointConfigurer enableDeviceAuthorizationEndpoint(boolean enableDeviceAuthorizationEndpoint) { + this.enableDeviceAuthorizationEndpoint = enableDeviceAuthorizationEndpoint; + return this; + } + @Override public void init(HttpSecurity builder) { AuthorizationServerSettings authorizationServerSettings = diff --git a/samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java b/samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java index 36682bb3f..39d62e73c 100644 --- a/samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java +++ b/samples/demo-authorizationserver/src/main/java/sample/config/AuthorizationServerConfig.java @@ -100,8 +100,10 @@ public SecurityFilterChain authorizationServerSecurityFilterChain( // @formatter:off http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) - .deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> - deviceAuthorizationEndpoint.verificationUri("/activate") + .deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { + deviceAuthorizationEndpoint.verificationUri("/activate"); + deviceAuthorizationEndpoint.enableDeviceAuthorizationEndpoint(true); + } ) .deviceVerificationEndpoint(deviceVerificationEndpoint -> deviceVerificationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI)