1
1
/*
2
- * Copyright 2002-2018 the original author or authors.
2
+ * Copyright 2002-2019 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
34
34
import org .springframework .security .config .oauth2 .client .CommonOAuth2Provider ;
35
35
import org .springframework .security .config .test .SpringTestRule ;
36
36
import org .springframework .security .htmlunit .server .WebTestClientHtmlUnitDriverBuilder ;
37
+ import org .springframework .security .oauth2 .client .authentication .OAuth2AuthorizationCodeAuthenticationToken ;
37
38
import org .springframework .security .oauth2 .client .authentication .OAuth2LoginAuthenticationToken ;
39
+ import org .springframework .security .oauth2 .client .endpoint .OAuth2AuthorizationCodeGrantRequest ;
40
+ import org .springframework .security .oauth2 .client .endpoint .ReactiveOAuth2AccessTokenResponseClient ;
41
+ import org .springframework .security .oauth2 .client .oidc .authentication .OidcAuthorizationCodeReactiveAuthenticationManager ;
42
+ import org .springframework .security .oauth2 .client .oidc .userinfo .OidcUserRequest ;
38
43
import org .springframework .security .oauth2 .client .registration .ClientRegistration ;
39
44
import org .springframework .security .oauth2 .client .registration .InMemoryReactiveClientRegistrationRepository ;
45
+ import org .springframework .security .oauth2 .client .userinfo .ReactiveOAuth2UserService ;
40
46
import org .springframework .security .oauth2 .core .OAuth2AccessToken ;
47
+ import org .springframework .security .oauth2 .core .OAuth2AuthenticationException ;
48
+ import org .springframework .security .oauth2 .core .OAuth2Error ;
41
49
import org .springframework .security .oauth2 .core .TestOAuth2AccessTokens ;
42
50
import org .springframework .security .oauth2 .core .endpoint .OAuth2AuthorizationExchange ;
51
+ import org .springframework .security .oauth2 .core .endpoint .OAuth2AuthorizationRequest ;
52
+ import org .springframework .security .oauth2 .core .endpoint .OAuth2AuthorizationResponse ;
43
53
import org .springframework .security .oauth2 .core .endpoint .TestOAuth2AuthorizationExchanges ;
54
+ import org .springframework .security .oauth2 .core .endpoint .TestOAuth2AuthorizationRequests ;
55
+ import org .springframework .security .oauth2 .core .endpoint .TestOAuth2AuthorizationResponses ;
56
+ import org .springframework .security .oauth2 .core .oidc .user .OidcUser ;
44
57
import org .springframework .security .oauth2 .core .user .OAuth2User ;
45
58
import org .springframework .security .oauth2 .core .user .TestOAuth2Users ;
46
59
import org .springframework .security .test .web .reactive .server .WebTestClientBuilder ;
54
67
55
68
import reactor .core .publisher .Mono ;
56
69
70
+ import java .time .Duration ;
71
+ import java .time .Instant ;
72
+
57
73
/**
58
74
* @author Rob Winch
59
75
* @since 5.1
@@ -72,6 +88,12 @@ public class OAuth2LoginTests {
72
88
.clientSecret ("secret" )
73
89
.build ();
74
90
91
+ private static ClientRegistration google = CommonOAuth2Provider .GOOGLE
92
+ .getBuilder ("google" )
93
+ .clientId ("client" )
94
+ .clientSecret ("secret" )
95
+ .build ();
96
+
75
97
@ Test
76
98
public void defaultLoginPageWithMultipleClientRegistrationsThenLinks () {
77
99
this .spring .register (OAuth2LoginWithMulitpleClientRegistrations .class ).autowire ();
@@ -97,11 +119,6 @@ public void defaultLoginPageWithMultipleClientRegistrationsThenLinks() {
97
119
static class OAuth2LoginWithMulitpleClientRegistrations {
98
120
@ Bean
99
121
InMemoryReactiveClientRegistrationRepository clientRegistrationRepository () {
100
- ClientRegistration google = CommonOAuth2Provider .GOOGLE
101
- .getBuilder ("google" )
102
- .clientId ("client" )
103
- .clientSecret ("secret" )
104
- .build ();
105
122
return new InMemoryReactiveClientRegistrationRepository (github , google );
106
123
}
107
124
}
@@ -182,6 +199,78 @@ public SecurityWebFilterChain springSecurityFilter(ServerHttpSecurity http) {
182
199
}
183
200
}
184
201
202
+ // gh-5562
203
+ @ Test
204
+ public void oauth2LoginWhenAccessTokenRequestFailsThenDefaultRedirectToLogin () {
205
+ this .spring .register (OAuth2LoginWithMulitpleClientRegistrations .class ,
206
+ OAuth2LoginWithCustomBeansConfig .class ).autowire ();
207
+
208
+ WebTestClient webTestClient = WebTestClientBuilder
209
+ .bindToWebFilters (this .springSecurity )
210
+ .build ();
211
+
212
+ OAuth2AuthorizationRequest request = TestOAuth2AuthorizationRequests .request ().scope ("openid" ).build ();
213
+ OAuth2AuthorizationResponse response = TestOAuth2AuthorizationResponses .success ().build ();
214
+ OAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange (request , response );
215
+ OAuth2AccessToken accessToken = new OAuth2AccessToken (
216
+ OAuth2AccessToken .TokenType .BEARER , "openid" , Instant .now (), Instant .now ().plus (Duration .ofDays (1 )));
217
+ OAuth2AuthorizationCodeAuthenticationToken authenticationToken =
218
+ new OAuth2AuthorizationCodeAuthenticationToken (google , exchange , accessToken );
219
+
220
+ OAuth2LoginWithCustomBeansConfig config = this .spring .getContext ().getBean (OAuth2LoginWithCustomBeansConfig .class );
221
+
222
+ ServerAuthenticationConverter converter = config .authenticationConverter ;
223
+ when (converter .convert (any ())).thenReturn (Mono .just (authenticationToken ));
224
+
225
+ ReactiveOAuth2AccessTokenResponseClient <OAuth2AuthorizationCodeGrantRequest > tokenResponseClient = config .tokenResponseClient ;
226
+ OAuth2Error oauth2Error = new OAuth2Error ("invalid_request" , "Invalid request" , null );
227
+ when (tokenResponseClient .getTokenResponse (any ())).thenThrow (new OAuth2AuthenticationException (oauth2Error ));
228
+
229
+ webTestClient .get ()
230
+ .uri ("/login/oauth2/code/google" )
231
+ .exchange ()
232
+ .expectStatus ()
233
+ .is3xxRedirection ()
234
+ .expectHeader ()
235
+ .valueEquals ("Location" , "/login?error" );
236
+ }
237
+
238
+ @ Configuration
239
+ static class OAuth2LoginWithCustomBeansConfig {
240
+
241
+ ServerAuthenticationConverter authenticationConverter = mock (ServerAuthenticationConverter .class );
242
+
243
+ ReactiveOAuth2AccessTokenResponseClient <OAuth2AuthorizationCodeGrantRequest > tokenResponseClient =
244
+ mock (ReactiveOAuth2AccessTokenResponseClient .class );
245
+
246
+ ReactiveOAuth2UserService <OidcUserRequest , OidcUser > userService = mock (ReactiveOAuth2UserService .class );
247
+
248
+ @ Bean
249
+ public SecurityWebFilterChain springSecurityFilter (ServerHttpSecurity http ) {
250
+ // @formatter:off
251
+ http
252
+ .authorizeExchange ()
253
+ .anyExchange ().authenticated ()
254
+ .and ()
255
+ .oauth2Login ()
256
+ .authenticationConverter (authenticationConverter )
257
+ .authenticationManager (authenticationManager ());
258
+ return http .build ();
259
+ // @formatter:on
260
+ }
261
+
262
+ private ReactiveAuthenticationManager authenticationManager () {
263
+ OidcAuthorizationCodeReactiveAuthenticationManager oidc =
264
+ new OidcAuthorizationCodeReactiveAuthenticationManager (tokenResponseClient , userService );
265
+ return oidc ;
266
+ }
267
+
268
+ @ Bean
269
+ public ReactiveOAuth2AccessTokenResponseClient <OAuth2AuthorizationCodeGrantRequest > accessTokenResponseClient () {
270
+ return tokenResponseClient ;
271
+ }
272
+ }
273
+
185
274
static class GitHubWebFilter implements WebFilter {
186
275
187
276
@ Override
0 commit comments