33
33
import org .springframework .security .config .annotation .web .configuration .EnableWebSecurity ;
34
34
import org .springframework .security .config .test .SpringTestContext ;
35
35
import org .springframework .security .config .test .SpringTestContextExtension ;
36
+ import org .springframework .security .core .authority .AuthorityUtils ;
36
37
import org .springframework .security .core .context .SecurityContextHolder ;
37
38
import org .springframework .security .core .context .SecurityContextImpl ;
39
+ import org .springframework .security .core .userdetails .User ;
38
40
import org .springframework .security .core .userdetails .UserDetailsService ;
39
41
import org .springframework .security .provisioning .InMemoryUserDetailsManager ;
40
42
import org .springframework .security .web .FilterChainProxy ;
41
43
import org .springframework .security .web .SecurityFilterChain ;
42
44
import org .springframework .security .web .authentication .ui .DefaultResourcesFilter ;
43
45
import org .springframework .security .web .webauthn .api .PublicKeyCredentialCreationOptions ;
46
+ import org .springframework .security .web .webauthn .api .PublicKeyCredentialRequestOptions ;
47
+ import org .springframework .security .web .webauthn .api .PublicKeyCredentialUserEntity ;
44
48
import org .springframework .security .web .webauthn .api .TestPublicKeyCredentialCreationOptions ;
49
+ import org .springframework .security .web .webauthn .api .TestPublicKeyCredentialRequestOptions ;
50
+ import org .springframework .security .web .webauthn .api .TestPublicKeyCredentialUserEntity ;
51
+ import org .springframework .security .web .webauthn .authentication .HttpSessionPublicKeyCredentialRequestOptionsRepository ;
45
52
import org .springframework .security .web .webauthn .management .WebAuthnRelyingPartyOperations ;
46
53
import org .springframework .security .web .webauthn .registration .HttpSessionPublicKeyCredentialCreationOptionsRepository ;
47
54
import org .springframework .test .web .servlet .MockMvc ;
@@ -67,6 +74,21 @@ public class WebAuthnConfigurerTests {
67
74
68
75
public final SpringTestContext spring = new SpringTestContext (this );
69
76
77
+ private static final String WEBAUTHN_LOGIN_BODY = """
78
+ {
79
+ "id": "dYF7EGnRFFIXkpXi9XU2wg",
80
+ "rawId": "dYF7EGnRFFIXkpXi9XU2wg",
81
+ "response": {
82
+ "authenticatorData": "y9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNgdAAAAAA",
83
+ "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRFVsRzRDbU9naWhKMG1vdXZFcE9HdUk0ZVJ6MGRRWmxUQmFtbjdHQ1FTNCIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
84
+ "signature": "MEYCIQCW2BcUkRCAXDmGxwMi78jknenZ7_amWrUJEYoTkweldAIhAMD0EMp1rw2GfwhdrsFIeDsL7tfOXVPwOtfqJntjAo4z",
85
+ "userHandle": "Q3_0Xd64_HW0BlKRAJnVagJTpLKLgARCj8zjugpRnVo"
86
+ },
87
+ "clientExtensionResults": {},
88
+ "authenticatorAttachment": "platform"
89
+ }
90
+ """ ;
91
+
70
92
@ Autowired
71
93
MockMvc mvc ;
72
94
@@ -182,6 +204,53 @@ public void webauthnWhenConfiguredPublicKeyCredentialCreationOptionsRepositoryBe
182
204
.andExpect (request ().sessionAttribute (attrName , options ));
183
205
}
184
206
207
+ @ Test
208
+ public void webauthnWhenConfiguredPublicKeyCredentialRequestOptionsRepositoryBeanPresent () throws Exception {
209
+ PublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions .create ()
210
+ .build ();
211
+ WebAuthnRelyingPartyOperations rpOperations = mock (WebAuthnRelyingPartyOperations .class );
212
+ ConfigCredentialRequestOptionsRepositoryFromBean .rpOperations = rpOperations ;
213
+ given (rpOperations .createCredentialRequestOptions (any ())).willReturn (options );
214
+ String attrName = "attrName" ;
215
+ HttpSessionPublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository ();
216
+ requestOptionsRepository .setAttrName (attrName );
217
+ ConfigCredentialRequestOptionsRepositoryFromBean .requestOptionsRepository = requestOptionsRepository ;
218
+ this .spring .register (ConfigCredentialRequestOptionsRepositoryFromBean .class ).autowire ();
219
+ this .mvc .perform (post ("/webauthn/authenticate/options" ))
220
+ .andExpect (status ().isOk ())
221
+ .andExpect (request ().sessionAttribute (attrName , options ));
222
+ PublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntity .userEntity ().build ();
223
+ given (rpOperations .authenticate (any ())).willReturn (userEntity );
224
+ this .mvc .perform (post ("/login/webauthn" )
225
+ .content (WEBAUTHN_LOGIN_BODY )
226
+ .sessionAttr (attrName , options ))
227
+ .andExpect (status ().isOk ());
228
+ }
229
+
230
+ @ Test
231
+ public void webauthnWhenConfiguredPublicKeyCredentialRequestOptionsRepository () throws Exception {
232
+ PublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions
233
+ .create ()
234
+ .build ();
235
+ WebAuthnRelyingPartyOperations rpOperations = mock (WebAuthnRelyingPartyOperations .class );
236
+ ConfigCredentialRequestOptionsRepository .rpOperations = rpOperations ;
237
+ given (rpOperations .createCredentialRequestOptions (any ())).willReturn (options );
238
+ String attrName = "attrName" ;
239
+ HttpSessionPublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository ();
240
+ requestOptionsRepository .setAttrName (attrName );
241
+ ConfigCredentialRequestOptionsRepository .requestOptionsRepository = requestOptionsRepository ;
242
+ this .spring .register (ConfigCredentialRequestOptionsRepository .class ).autowire ();
243
+ this .mvc .perform (post ("/webauthn/authenticate/options" ))
244
+ .andExpect (status ().isOk ())
245
+ .andExpect (request ().sessionAttribute (attrName , options ));
246
+ PublicKeyCredentialUserEntity userEntity = TestPublicKeyCredentialUserEntity .userEntity ().build ();
247
+ given (rpOperations .authenticate (any ())).willReturn (userEntity );
248
+ this .mvc .perform (post ("/login/webauthn" )
249
+ .content (WEBAUTHN_LOGIN_BODY )
250
+ .sessionAttr (attrName , options ))
251
+ .andExpect (status ().isOk ());
252
+ }
253
+
185
254
@ Test
186
255
public void webauthnWhenConfiguredMessageConverter () throws Exception {
187
256
TestingAuthenticationToken user = new TestingAuthenticationToken ("user" , "password" , "ROLE_USER" );
@@ -264,6 +333,74 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
264
333
265
334
}
266
335
336
+ @ Configuration
337
+ @ EnableWebSecurity
338
+ static class ConfigCredentialRequestOptionsRepositoryFromBean {
339
+
340
+ private static HttpSessionPublicKeyCredentialRequestOptionsRepository requestOptionsRepository ;
341
+
342
+ private static WebAuthnRelyingPartyOperations rpOperations ;
343
+
344
+ @ Bean
345
+ WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations () {
346
+ return ConfigCredentialRequestOptionsRepositoryFromBean .rpOperations ;
347
+ }
348
+
349
+ @ Bean
350
+ UserDetailsService userDetailsService () {
351
+ InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager ();
352
+ userDetailsService .createUser (User .builder ().username ("user" )
353
+ .password ("{noop}password" )
354
+ .authorities (AuthorityUtils .createAuthorityList ("ROLE_USER" ))
355
+ .build ());
356
+ return userDetailsService ;
357
+ }
358
+
359
+ @ Bean
360
+ HttpSessionPublicKeyCredentialRequestOptionsRepository credentialRequestOptionsRepository () {
361
+ return ConfigCredentialRequestOptionsRepositoryFromBean .requestOptionsRepository ;
362
+ }
363
+
364
+ @ Bean
365
+ SecurityFilterChain securityFilterChain (HttpSecurity http ) throws Exception {
366
+ return http .csrf (AbstractHttpConfigurer ::disable ).webAuthn (Customizer .withDefaults ()).build ();
367
+ }
368
+
369
+ }
370
+
371
+ @ Configuration
372
+ @ EnableWebSecurity
373
+ static class ConfigCredentialRequestOptionsRepository {
374
+
375
+ private static HttpSessionPublicKeyCredentialRequestOptionsRepository requestOptionsRepository ;
376
+
377
+ private static WebAuthnRelyingPartyOperations rpOperations ;
378
+
379
+ @ Bean
380
+ WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations () {
381
+ return ConfigCredentialRequestOptionsRepository .rpOperations ;
382
+ }
383
+
384
+ @ Bean
385
+ UserDetailsService userDetailsService () {
386
+ InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager ();
387
+ userDetailsService .createUser (User .builder ().username ("user" )
388
+ .password ("{noop}password" )
389
+ .authorities (AuthorityUtils .createAuthorityList ("ROLE_USER" ))
390
+ .build ());
391
+ return userDetailsService ;
392
+ }
393
+
394
+ @ Bean
395
+ SecurityFilterChain securityFilterChain (HttpSecurity http ) throws Exception {
396
+ return http .csrf (AbstractHttpConfigurer ::disable )
397
+ .webAuthn ((c ) -> c .requestOptionsRepository (requestOptionsRepository ))
398
+ .build ();
399
+ }
400
+
401
+ }
402
+
403
+
267
404
@ Configuration
268
405
@ EnableWebSecurity
269
406
static class ConfigMessageConverter {
0 commit comments