Skip to content

Commit 0848c7d

Browse files
author
Greg Li
committed
Disable device authorization grant by default. Such as, deviceAuthorizationEndpoint and deviceVerificationEndpoint
1 parent 4e56073 commit 0848c7d

File tree

4 files changed

+110
-25
lines changed

4 files changed

+110
-25
lines changed

Diff for: docs/src/test/java/sample/jpa/JpaTests.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
* Tests for the guide How-to: Implement core services with JPA.
7070
*
7171
* @author Steve Riesenberg
72+
* @author Greg Li
7273
*/
7374
@ExtendWith(SpringTestContextExtension.class)
7475
public class JpaTests {
@@ -88,7 +89,7 @@ public class JpaTests {
8889

8990
@Test
9091
public void oidcLoginWhenJpaCoreServicesAutowiredThenUsed() throws Exception {
91-
this.spring.register(AuthorizationServerConfig.class).autowire();
92+
this.spring.register(AuthorizationServerConfigDeviceAuthorize.class).autowire();
9293
assertThat(this.registeredClientRepository).isInstanceOf(JpaRegisteredClientRepository.class);
9394
assertThat(this.authorizationService).isInstanceOf(JpaOAuth2AuthorizationService.class);
9495
assertThat(this.authorizationConsentService).isInstanceOf(JpaOAuth2AuthorizationConsentService.class);
@@ -135,7 +136,7 @@ public void oidcLoginWhenJpaCoreServicesAutowiredThenUsed() throws Exception {
135136

136137
@Test
137138
public void deviceAuthorizationWhenJpaCoreServicesAutowiredThenSuccess() throws Exception {
138-
this.spring.register(AuthorizationServerConfig.class).autowire();
139+
this.spring.register(AuthorizationServerConfigDeviceAuthorize.class).autowire();
139140
assertThat(this.registeredClientRepository).isInstanceOf(JpaRegisteredClientRepository.class);
140141
assertThat(this.authorizationService).isInstanceOf(JpaOAuth2AuthorizationService.class);
141142
assertThat(this.authorizationConsentService).isInstanceOf(JpaOAuth2AuthorizationConsentService.class);
@@ -191,13 +192,15 @@ private OAuth2Authorization findAuthorization(String token, String tokenType) {
191192
@EnableWebSecurity
192193
@EnableAutoConfiguration
193194
@ComponentScan
194-
static class AuthorizationServerConfig {
195+
static class AuthorizationServerConfigDeviceAuthorize {
195196

196197
@Bean
197198
@Order(Ordered.HIGHEST_PRECEDENCE)
198199
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
199200
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
200201
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
202+
.deviceAuthorizationEndpoint(Customizer.withDefaults())
203+
.deviceVerificationEndpoint(Customizer.withDefaults())
201204
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
202205

203206
// @formatter:off

Diff for: oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java

+54-11
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
4646
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
4747
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
48+
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContext;
49+
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
4850
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
4951
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
5052
import org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter;
@@ -55,6 +57,7 @@
5557
import org.springframework.security.web.util.matcher.OrRequestMatcher;
5658
import org.springframework.security.web.util.matcher.RequestMatcher;
5759
import org.springframework.util.Assert;
60+
import org.springframework.web.util.UriComponentsBuilder;
5861

5962
/**
6063
* An {@link AbstractHttpConfigurer} for OAuth 2.0 Authorization Server support.
@@ -64,6 +67,7 @@
6467
* @author Gerardo Roza
6568
* @author Ovidiu Popa
6669
* @author Gaurav Tiwari
70+
* @author Greg Li
6771
* @since 0.0.1
6872
* @see AbstractHttpConfigurer
6973
* @see OAuth2ClientAuthenticationConfigurer
@@ -218,26 +222,40 @@ public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(Customizer<OA
218222
}
219223

220224
/**
221-
* Configures the OAuth 2.0 Device Authorization Endpoint.
225+
* Configures the OAuth 2.0 Device Authorization Endpoint (disabled by default).
222226
*
223227
* @param deviceAuthorizationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceAuthorizationEndpointConfigurer}
224228
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
225229
* @since 1.1
226230
*/
227231
public OAuth2AuthorizationServerConfigurer deviceAuthorizationEndpoint(Customizer<OAuth2DeviceAuthorizationEndpointConfigurer> deviceAuthorizationEndpointCustomizer) {
228-
deviceAuthorizationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class));
232+
OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer =
233+
getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class);
234+
if (deviceAuthorizationEndpointConfigurer == null) {
235+
addConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class,
236+
new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess));
237+
deviceAuthorizationEndpointConfigurer = getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class);
238+
}
239+
deviceAuthorizationEndpointCustomizer.customize(deviceAuthorizationEndpointConfigurer);
229240
return this;
230241
}
231242

232243
/**
233-
* Configures the OAuth 2.0 Device Verification Endpoint.
244+
* Configures the OAuth 2.0 Device Verification Endpoint (disabled by default).
234245
*
235246
* @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceVerificationEndpointConfigurer}
236247
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
237248
* @since 1.1
238249
*/
239250
public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(Customizer<OAuth2DeviceVerificationEndpointConfigurer> deviceVerificationEndpointCustomizer) {
240-
deviceVerificationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class));
251+
OAuth2DeviceVerificationEndpointConfigurer deviceVerificationEndpointConfigurer =
252+
getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class);
253+
if (deviceVerificationEndpointConfigurer == null) {
254+
addConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class,
255+
new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess));
256+
deviceVerificationEndpointConfigurer = getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class);
257+
}
258+
deviceVerificationEndpointCustomizer.customize(deviceVerificationEndpointConfigurer);
241259
return this;
242260
}
243261

@@ -319,19 +337,45 @@ public void init(HttpSecurity httpSecurity) {
319337

320338
ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = httpSecurity.getConfigurer(ExceptionHandlingConfigurer.class);
321339
if (exceptionHandling != null) {
340+
OrRequestMatcher preferredRequestMatcher = null;
341+
if (getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class) != null) {
342+
preferredRequestMatcher = new OrRequestMatcher(
343+
getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
344+
getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class),
345+
getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class),
346+
getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class));
347+
} else {
348+
preferredRequestMatcher = new OrRequestMatcher(
349+
getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
350+
getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class),
351+
getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class));
352+
}
322353
exceptionHandling.defaultAuthenticationEntryPointFor(
323354
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
324-
new OrRequestMatcher(
325-
getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
326-
getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class),
327-
getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class),
328-
getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class))
355+
preferredRequestMatcher
329356
);
330357
}
331358
}
332359

333360
@Override
334361
public void configure(HttpSecurity httpSecurity) {
362+
OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationEndpointConfigurer =
363+
getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class);
364+
if (deviceAuthorizationEndpointConfigurer != null) {
365+
OAuth2AuthorizationServerMetadataEndpointConfigurer auth2AuthorizationServerMetadataEndpointConfigurer =
366+
getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class);
367+
368+
auth2AuthorizationServerMetadataEndpointConfigurer
369+
.addDefaultAuthorizationServerMetadataCustomizer((builder) -> {
370+
AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();
371+
String issuer = authorizationServerContext.getIssuer();
372+
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings();
373+
String deviceAuthorizationEndpoint = UriComponentsBuilder.fromUriString(issuer)
374+
.path(authorizationServerSettings.getDeviceAuthorizationEndpoint()).build().toUriString();
375+
376+
builder.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint);
377+
});
378+
}
335379
this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity));
336380

337381
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
@@ -359,8 +403,7 @@ private Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer>
359403
configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess));
360404
configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess));
361405
configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess));
362-
configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class, new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess));
363-
configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess));
406+
//configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess));
364407
return configurers;
365408
}
366409

Diff for: oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
*
4747
* @author Daniel Garnier-Moiroux
4848
* @author Joe Grandja
49+
* @author Greg Li
4950
* @since 0.1.1
5051
* @see OAuth2AuthorizationServerMetadata
5152
* @see AuthorizationServerSettings
@@ -92,7 +93,6 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
9293
OAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder()
9394
.issuer(issuer)
9495
.authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint()))
95-
.deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint()))
9696
.tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint()))
9797
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
9898
.jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint()))

Diff for: oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java

+49-10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import org.springframework.beans.factory.annotation.Autowired;
3434
import org.springframework.context.annotation.Bean;
3535
import org.springframework.context.annotation.Import;
36+
import org.springframework.core.Ordered;
37+
import org.springframework.core.annotation.Order;
3638
import org.springframework.http.HttpHeaders;
3739
import org.springframework.http.HttpStatus;
3840
import org.springframework.http.MediaType;
@@ -45,6 +47,8 @@
4547
import org.springframework.mock.http.client.MockClientHttpResponse;
4648
import org.springframework.mock.web.MockHttpServletResponse;
4749
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
50+
import org.springframework.security.config.Customizer;
51+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
4852
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
4953
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
5054
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -72,6 +76,7 @@
7276
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
7377
import org.springframework.security.oauth2.server.authorization.test.SpringTestContext;
7478
import org.springframework.security.oauth2.server.authorization.test.SpringTestContextExtension;
79+
import org.springframework.security.web.SecurityFilterChain;
7580
import org.springframework.test.web.servlet.MockMvc;
7681
import org.springframework.test.web.servlet.MvcResult;
7782
import org.springframework.util.LinkedMultiValueMap;
@@ -90,6 +95,7 @@
9095
* Integration tests for OAuth 2.0 Device Grant.
9196
*
9297
* @author Steve Riesenberg
98+
* @author Greg Li
9399
*/
94100
@ExtendWith(SpringTestContextExtension.class)
95101
public class OAuth2DeviceCodeGrantTests {
@@ -158,7 +164,7 @@ public static void destroy() {
158164

159165
@Test
160166
public void requestWhenDeviceAuthorizationRequestNotAuthenticatedThenUnauthorized() throws Exception {
161-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
167+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
162168

163169
// @formatter:off
164170
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -179,9 +185,32 @@ public void requestWhenDeviceAuthorizationRequestNotAuthenticatedThenUnauthorize
179185
// @formatter:on
180186
}
181187

188+
@Test
189+
public void requestWhenDeviceAuthorizationRequestDisabledThenUnauthorized() throws Exception {
190+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
191+
192+
// @formatter:off
193+
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
194+
.authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)
195+
.build();
196+
// @formatter:on
197+
this.registeredClientRepository.save(registeredClient);
198+
199+
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
200+
parameters.set(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId());
201+
parameters.set(OAuth2ParameterNames.SCOPE,
202+
StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " "));
203+
204+
// @formatter:off
205+
this.mvc.perform(post(DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI)
206+
.params(parameters))
207+
.andExpect(status().isUnauthorized());
208+
// @formatter:on
209+
}
210+
182211
@Test
183212
public void requestWhenRegisteredClientMissingThenUnauthorized() throws Exception {
184-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
213+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
185214

186215
// @formatter:off
187216
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -204,7 +233,7 @@ public void requestWhenRegisteredClientMissingThenUnauthorized() throws Exceptio
204233

205234
@Test
206235
public void requestWhenDeviceAuthorizationRequestValidThenReturnDeviceAuthorizationResponse() throws Exception {
207-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
236+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
208237

209238
// @formatter:off
210239
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -252,7 +281,7 @@ public void requestWhenDeviceAuthorizationRequestValidThenReturnDeviceAuthorizat
252281

253282
@Test
254283
public void requestWhenDeviceVerificationRequestUnauthenticatedThenUnauthorized() throws Exception {
255-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
284+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
256285

257286
// @formatter:off
258287
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -286,7 +315,7 @@ public void requestWhenDeviceVerificationRequestUnauthenticatedThenUnauthorized(
286315

287316
@Test
288317
public void requestWhenDeviceVerificationRequestValidThenDisplaysConsentPage() throws Exception {
289-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
318+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
290319

291320
// @formatter:off
292321
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -335,7 +364,7 @@ public void requestWhenDeviceVerificationRequestValidThenDisplaysConsentPage() t
335364

336365
@Test
337366
public void requestWhenDeviceAuthorizationConsentRequestUnauthenticatedThenBadRequest() throws Exception {
338-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
367+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
339368

340369
// @formatter:off
341370
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -373,7 +402,7 @@ public void requestWhenDeviceAuthorizationConsentRequestUnauthenticatedThenBadRe
373402

374403
@Test
375404
public void requestWhenDeviceAuthorizationConsentRequestValidThenRedirectsToSuccessPage() throws Exception {
376-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
405+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
377406

378407
// @formatter:off
379408
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -423,7 +452,7 @@ public void requestWhenDeviceAuthorizationConsentRequestValidThenRedirectsToSucc
423452

424453
@Test
425454
public void requestWhenAccessTokenRequestUnauthenticatedThenUnauthorized() throws Exception {
426-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
455+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
427456

428457
// @formatter:off
429458
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -459,7 +488,7 @@ public void requestWhenAccessTokenRequestUnauthenticatedThenUnauthorized() throw
459488

460489
@Test
461490
public void requestWhenAccessTokenRequestValidThenReturnAccessTokenResponse() throws Exception {
462-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
491+
this.spring.register(AuthorizationServerConfigurationDeviceAuthorize.class).autowire();
463492

464493
// @formatter:off
465494
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
@@ -545,7 +574,17 @@ private static Function<OAuth2Authorization.Token<? extends OAuth2Token>, Boolea
545574

546575
@EnableWebSecurity
547576
@Import(OAuth2AuthorizationServerConfiguration.class)
548-
static class AuthorizationServerConfiguration {
577+
static class AuthorizationServerConfigurationDeviceAuthorize {
578+
579+
@Bean
580+
@Order(Ordered.HIGHEST_PRECEDENCE)
581+
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
582+
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
583+
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
584+
.deviceAuthorizationEndpoint(Customizer.withDefaults()) // Enable deviceAuthorizationEndpoint
585+
.deviceVerificationEndpoint(Customizer.withDefaults()); // Enable deviceVerificationEndpoint
586+
return http.build();
587+
}
549588

550589
@Bean
551590
RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {

0 commit comments

Comments
 (0)