19
19
import java .io .IOException ;
20
20
import java .time .Duration ;
21
21
import java .time .Instant ;
22
- import java .time .ZoneOffset ;
23
22
24
23
import jakarta .servlet .ServletException ;
25
24
import jakarta .servlet .http .HttpServletRequest ;
32
31
import org .springframework .context .annotation .Bean ;
33
32
import org .springframework .context .annotation .Configuration ;
34
33
import org .springframework .context .annotation .Import ;
34
+ import org .springframework .security .authentication .ott .DefaultOneTimeToken ;
35
35
import org .springframework .security .authentication .ott .GenerateOneTimeTokenRequest ;
36
36
import org .springframework .security .authentication .ott .OneTimeToken ;
37
+ import org .springframework .security .authentication .ott .OneTimeTokenService ;
37
38
import org .springframework .security .config .Customizer ;
38
39
import org .springframework .security .config .annotation .web .builders .HttpSecurity ;
39
40
import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
44
45
import org .springframework .security .provisioning .InMemoryUserDetailsManager ;
45
46
import org .springframework .security .web .SecurityFilterChain ;
46
47
import org .springframework .security .web .authentication .SimpleUrlAuthenticationSuccessHandler ;
47
- import org .springframework .security .web .authentication .ott .DefaultGenerateOneTimeTokenRequestResolver ;
48
48
import org .springframework .security .web .authentication .ott .GenerateOneTimeTokenRequestResolver ;
49
49
import org .springframework .security .web .authentication .ott .OneTimeTokenGenerationSuccessHandler ;
50
50
import org .springframework .security .web .authentication .ott .RedirectOneTimeTokenGenerationSuccessHandler ;
55
55
56
56
import static org .assertj .core .api .Assertions .assertThat ;
57
57
import static org .assertj .core .api .Assertions .assertThatException ;
58
+ import static org .mockito .ArgumentMatchers .any ;
59
+ import static org .mockito .ArgumentMatchers .eq ;
60
+ import static org .mockito .BDDMockito .given ;
61
+ import static org .mockito .Mockito .mock ;
62
+ import static org .mockito .Mockito .verify ;
58
63
import static org .springframework .security .test .web .servlet .request .SecurityMockMvcRequestPostProcessors .csrf ;
59
64
import static org .springframework .security .test .web .servlet .response .SecurityMockMvcResultMatchers .authenticated ;
60
65
import static org .springframework .security .test .web .servlet .response .SecurityMockMvcResultMatchers .unauthenticated ;
@@ -72,6 +77,15 @@ public class OneTimeTokenLoginConfigurerTests {
72
77
@ Autowired (required = false )
73
78
MockMvc mvc ;
74
79
80
+ @ Autowired (required = false )
81
+ private GenerateOneTimeTokenRequestResolver resolver ;
82
+
83
+ @ Autowired (required = false )
84
+ private OneTimeTokenService tokenService ;
85
+
86
+ @ Autowired (required = false )
87
+ private OneTimeTokenGenerationSuccessHandler tokenGenerationSuccessHandler ;
88
+
75
89
@ Test
76
90
void oneTimeTokenWhenCorrectTokenThenCanAuthenticate () throws Exception {
77
91
this .spring .register (OneTimeTokenDefaultConfig .class ).autowire ();
@@ -202,21 +216,18 @@ Please provide it as a bean or pass it to the oneTimeTokenLogin() DSL.
202
216
203
217
@ Test
204
218
void oneTimeTokenWhenCustomTokenExpirationTimeSetThenAuthenticate () throws Exception {
205
- this .spring .register (OneTimeTokenConfigWithCustomTokenExpirationTime .class ).autowire ();
206
- this .mvc .perform (post ("/ott/generate" ).param ("username" , "user" ).with (csrf ()))
207
- .andExpectAll (status ().isFound (), redirectedUrl ("/login/ott" ));
208
-
209
- OneTimeToken token = getLastToken ();
210
-
211
- this .mvc .perform (post ("/login/ott" ).param ("token" , token .getTokenValue ()).with (csrf ()))
212
- .andExpectAll (status ().isFound (), redirectedUrl ("/" ), authenticated ());
213
- assertThat (getCurrentMinutes (token .getExpiresAt ())).isEqualTo (10 );
214
- }
215
-
216
- private int getCurrentMinutes (Instant expiresAt ) {
217
- int expiresMinutes = expiresAt .atZone (ZoneOffset .UTC ).getMinute ();
218
- int currentMinutes = Instant .now ().atZone (ZoneOffset .UTC ).getMinute ();
219
- return expiresMinutes - currentMinutes ;
219
+ this .spring .register (OneTimeTokenConfigWithCustomImpls .class ).autowire ();
220
+ GenerateOneTimeTokenRequest expectedGenerateRequest = new GenerateOneTimeTokenRequest ("username-123" ,
221
+ Duration .ofMinutes (10 ));
222
+ OneTimeToken ott = new DefaultOneTimeToken ("token-123" , expectedGenerateRequest .getUsername (),
223
+ Instant .now ().plus (expectedGenerateRequest .getExpiresIn ()));
224
+ given (this .resolver .resolve (any ())).willReturn (expectedGenerateRequest );
225
+ given (this .tokenService .generate (expectedGenerateRequest )).willReturn (ott );
226
+ this .mvc .perform (post ("/ott/generate" ).param ("username" , "user" ).with (csrf ()));
227
+
228
+ verify (this .resolver ).resolve (any ());
229
+ verify (this .tokenService ).generate (expectedGenerateRequest );
230
+ verify (this .tokenGenerationSuccessHandler ).handle (any (), any (), eq (ott ));
220
231
}
221
232
222
233
private OneTimeToken getLastToken () {
@@ -228,35 +239,40 @@ private OneTimeToken getLastToken() {
228
239
@ Configuration (proxyBeanMethods = false )
229
240
@ EnableWebSecurity
230
241
@ Import (UserDetailsServiceConfig .class )
231
- static class OneTimeTokenConfigWithCustomTokenExpirationTime {
242
+ static class OneTimeTokenConfigWithCustomImpls {
232
243
233
244
@ Bean
234
245
SecurityFilterChain securityFilterChain (HttpSecurity http ,
246
+ GenerateOneTimeTokenRequestResolver ottRequestResolver , OneTimeTokenService ottTokenService ,
235
247
OneTimeTokenGenerationSuccessHandler ottSuccessHandler ) throws Exception {
248
+
236
249
// @formatter:off
237
- http
250
+ http
238
251
.authorizeHttpRequests ((authz ) -> authz
239
252
.anyRequest ().authenticated ()
240
253
)
241
254
.oneTimeTokenLogin ((ott ) -> ott
255
+ .generateRequestResolver (ottRequestResolver )
256
+ .tokenService (ottTokenService )
242
257
.tokenGenerationSuccessHandler (ottSuccessHandler )
243
258
);
244
259
// @formatter:on
245
260
return http .build ();
246
261
}
247
262
248
263
@ Bean
249
- TestOneTimeTokenGenerationSuccessHandler ottSuccessHandler () {
250
- return new TestOneTimeTokenGenerationSuccessHandler ( );
264
+ GenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver () {
265
+ return mock ( GenerateOneTimeTokenRequestResolver . class );
251
266
}
252
267
253
268
@ Bean
254
- GenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver () {
255
- DefaultGenerateOneTimeTokenRequestResolver delegate = new DefaultGenerateOneTimeTokenRequestResolver ();
256
- return (request ) -> {
257
- GenerateOneTimeTokenRequest generate = delegate .resolve (request );
258
- return new GenerateOneTimeTokenRequest (generate .getUsername (), Duration .ofSeconds (600 ));
259
- };
269
+ OneTimeTokenService ottService () {
270
+ return mock (OneTimeTokenService .class );
271
+ }
272
+
273
+ @ Bean
274
+ OneTimeTokenGenerationSuccessHandler ottSuccessHandler () {
275
+ return mock (OneTimeTokenGenerationSuccessHandler .class );
260
276
}
261
277
262
278
}
0 commit comments