16
16
17
17
package org .springframework .security .webauthn .sample .app .web ;
18
18
19
+ import com .webauthn4j .util .Base64UrlUtil ;
19
20
import com .webauthn4j .util .UUIDUtil ;
21
+ import org .apache .commons .logging .Log ;
22
+ import org .apache .commons .logging .LogFactory ;
20
23
import org .springframework .beans .factory .annotation .Autowired ;
21
24
import org .springframework .security .authentication .MultiFactorAuthenticationToken ;
22
25
import org .springframework .security .core .Authentication ;
26
+ import org .springframework .security .core .GrantedAuthority ;
23
27
import org .springframework .security .core .context .SecurityContextHolder ;
24
28
import org .springframework .security .crypto .password .PasswordEncoder ;
25
- import org .springframework .security .webauthn .WebAuthnRegistrationRequestValidationResponse ;
29
+ import org .springframework .security .webauthn .WebAuthnDataConverter ;
30
+ import org .springframework .security .webauthn .WebAuthnOptionWebHelper ;
31
+ import org .springframework .security .webauthn .WebAuthnRegistrationRequest ;
26
32
import org .springframework .security .webauthn .WebAuthnRegistrationRequestValidator ;
33
+ import org .springframework .security .webauthn .authenticator .WebAuthnAuthenticator ;
34
+ import org .springframework .security .webauthn .authenticator .WebAuthnAuthenticatorImpl ;
35
+ import org .springframework .security .webauthn .authenticator .WebAuthnAuthenticatorTransport ;
36
+ import org .springframework .security .webauthn .exception .DataConversionException ;
27
37
import org .springframework .security .webauthn .exception .ValidationException ;
28
- import org .springframework .security .webauthn .sample .domain .entity .AuthenticatorEntity ;
29
- import org .springframework .security .webauthn .sample .domain .entity .UserEntity ;
30
- import org .springframework .security .webauthn .sample .domain .exception .WebAuthnSampleBusinessException ;
31
- import org .springframework .security .webauthn .sample .domain .service .WebAuthnUserDetailsServiceImpl ;
38
+ import org .springframework .security .webauthn .userdetails .InMemoryWebAuthnAndPasswordUserDetailsManager ;
39
+ import org .springframework .security .webauthn .userdetails .WebAuthnAndPasswordUser ;
32
40
import org .springframework .stereotype .Controller ;
33
41
import org .springframework .ui .Model ;
34
42
import org .springframework .util .Base64Utils ;
35
43
import org .springframework .validation .BindingResult ;
36
44
import org .springframework .web .bind .annotation .ModelAttribute ;
37
45
import org .springframework .web .bind .annotation .RequestMapping ;
38
46
import org .springframework .web .bind .annotation .RequestMethod ;
39
- import org .springframework .web .servlet .mvc .support .RedirectAttributes ;
40
47
41
48
import javax .servlet .http .HttpServletRequest ;
42
- import javax .servlet .http .HttpServletResponse ;
43
49
import javax .validation .Valid ;
44
- import java .util .ArrayList ;
45
- import java .util .List ;
46
- import java .util .UUID ;
50
+ import java .util .*;
51
+ import java .util .stream .Collectors ;
47
52
48
53
/**
49
54
* Login controller
52
57
@ Controller
53
58
public class WebAuthnSampleController {
54
59
60
+ private final Log logger = LogFactory .getLog (getClass ());
61
+
55
62
private static final String REDIRECT_LOGIN = "redirect:/login" ;
56
63
private static final String REDIRECT_SIGNUP = "redirect:/signup" ;
57
64
@@ -62,14 +69,26 @@ public class WebAuthnSampleController {
62
69
private static final String VIEW_DASHBOARD_DASHBOARD = "dashboard/dashboard" ;
63
70
64
71
@ Autowired
65
- private WebAuthnUserDetailsServiceImpl webAuthnUserDetailsService ;
72
+ private InMemoryWebAuthnAndPasswordUserDetailsManager webAuthnUserDetailsService ;
66
73
67
74
@ Autowired
68
75
private WebAuthnRegistrationRequestValidator registrationRequestValidator ;
69
76
77
+ @ Autowired
78
+ private WebAuthnOptionWebHelper webAuthnOptionWebHelper ;
79
+
80
+ @ Autowired
81
+ private WebAuthnDataConverter webAuthnDataConverter ;
82
+
70
83
@ Autowired
71
84
private PasswordEncoder passwordEncoder ;
72
85
86
+ @ ModelAttribute
87
+ public void addAttributes (Model model , HttpServletRequest request ) {
88
+ model .addAttribute ("webAuthnChallenge" , webAuthnOptionWebHelper .getChallenge (request ));
89
+ model .addAttribute ("webAuthnCredentialIds" , webAuthnOptionWebHelper .getCredentialIds ());
90
+ }
91
+
73
92
@ RequestMapping (value = "/" )
74
93
public String index (Model model ) {
75
94
return REDIRECT_SIGNUP ;
@@ -91,49 +110,70 @@ public String template(Model model) {
91
110
}
92
111
93
112
@ RequestMapping (value = "/signup" , method = RequestMethod .POST )
94
- public String create (HttpServletRequest request , HttpServletResponse response , @ Valid @ ModelAttribute ("userForm" ) UserCreateForm userCreateForm , BindingResult result , Model model , RedirectAttributes redirectAttributes ) {
113
+ public String create (HttpServletRequest request , @ Valid @ ModelAttribute ("userForm" ) UserCreateForm userCreateForm , BindingResult result , Model model ) {
95
114
96
115
if (result .hasErrors ()) {
97
116
return VIEW_SIGNUP_SIGNUP ;
98
117
}
99
- WebAuthnRegistrationRequestValidationResponse webAuthnRegistrationRequestValidationResponse ;
118
+
119
+ WebAuthnRegistrationRequest webAuthnRegistrationRequest = new WebAuthnRegistrationRequest (
120
+ request ,
121
+ userCreateForm .getAuthenticator ().getClientDataJSON (),
122
+ userCreateForm .getAuthenticator ().getAttestationObject (),
123
+ userCreateForm .getAuthenticator ().getTransports (),
124
+ userCreateForm .getAuthenticator ().getClientExtensions ()
125
+ );
100
126
try {
101
- webAuthnRegistrationRequestValidationResponse = registrationRequestValidator .validate (
102
- request ,
103
- userCreateForm .getAuthenticator ().getClientDataJSON (),
104
- userCreateForm .getAuthenticator ().getAttestationObject (),
105
- null , //TODO
106
- userCreateForm .getAuthenticator ().getClientExtensions ()
107
- );
108
- } catch (ValidationException e ) {
127
+ registrationRequestValidator .validate (webAuthnRegistrationRequest );
128
+ }
129
+ catch (ValidationException e ){
130
+ logger .debug ("WebAuthn registration request validation failed." , e );
109
131
return VIEW_SIGNUP_SIGNUP ;
110
132
}
133
+ catch (DataConversionException e ){
134
+ logger .debug ("WebAuthn registration request data conversion failed." , e );
135
+ return VIEW_SIGNUP_SIGNUP ;
136
+ }
137
+
138
+ AuthenticatorCreateForm sourceAuthenticator = userCreateForm .getAuthenticator ();
111
139
112
- UserEntity destination = new UserEntity ();
140
+ byte [] attestationObject = Base64UrlUtil .decode (sourceAuthenticator .getAttestationObject ());
141
+ byte [] authenticatorData = webAuthnDataConverter .extractAuthenticatorData (attestationObject );
142
+ byte [] attestedCredentialData = webAuthnDataConverter .extractAttestedCredentialData (authenticatorData );
143
+ byte [] credentialId = webAuthnDataConverter .extractCredentialId (attestedCredentialData );
144
+ long signCount = webAuthnDataConverter .extractSignCount (authenticatorData );
145
+ Set <WebAuthnAuthenticatorTransport > transports ;
146
+ if (sourceAuthenticator .getTransports () == null ) {
147
+ transports = null ;
148
+ }
149
+ else {
150
+ transports = sourceAuthenticator .getTransports ().stream ()
151
+ .map (WebAuthnAuthenticatorTransport ::create )
152
+ .collect (Collectors .toSet ());
153
+ }
113
154
114
- destination .setUserHandle (Base64Utils .decodeFromUrlSafeString (userCreateForm .getUserHandle ()));
115
- destination .setUsername (userCreateForm .getUsername ());
116
- destination .setPassword (passwordEncoder .encode (userCreateForm .getPassword ()));
155
+ List <WebAuthnAuthenticator > authenticators = new ArrayList <>();
156
+ WebAuthnAuthenticator authenticator = new WebAuthnAuthenticatorImpl (
157
+ credentialId ,
158
+ null ,
159
+ attestationObject ,
160
+ signCount ,
161
+ transports ,
162
+ sourceAuthenticator .getClientExtensions ());
117
163
118
- List <AuthenticatorEntity > authenticators = new ArrayList <>();
119
- AuthenticatorEntity authenticator = new AuthenticatorEntity ();
120
- AuthenticatorCreateForm sourceAuthenticator = userCreateForm .getAuthenticator ();
121
- authenticator .setUser (destination );
122
- authenticator .setName (null ); // sample application doesn't name authenticator
123
- authenticator .setAttestationStatement (webAuthnRegistrationRequestValidationResponse .getAttestationObject ().getAttestationStatement ());
124
- authenticator .setAttestedCredentialData (webAuthnRegistrationRequestValidationResponse .getAttestationObject ().getAuthenticatorData ().getAttestedCredentialData ());
125
- authenticator .setCounter (webAuthnRegistrationRequestValidationResponse .getAttestationObject ().getAuthenticatorData ().getSignCount ());
126
- authenticator .setTransports (sourceAuthenticator .getTransports ());
127
164
authenticators .add (authenticator );
128
165
129
- destination .setAuthenticators (authenticators );
130
- destination .setLocked (false );
131
- destination .setSingleFactorAuthenticationAllowed (userCreateForm .isSingleFactorAuthenticationAllowed ());
166
+ byte [] userHandle = Base64Utils .decodeFromUrlSafeString (userCreateForm .getUserHandle ());
167
+ String username = userCreateForm .getUsername ();
168
+ String password = passwordEncoder .encode (userCreateForm .getPassword ());
169
+ List <GrantedAuthority > authorities = Collections .emptyList ();
170
+ boolean singleFactorAuthenticationAllowed = userCreateForm .isSingleFactorAuthenticationAllowed ();
171
+ WebAuthnAndPasswordUser user = new WebAuthnAndPasswordUser (userHandle , username , password , authenticators , singleFactorAuthenticationAllowed , authorities );
172
+
132
173
133
- UserEntity user = destination ;
134
174
try {
135
175
webAuthnUserDetailsService .createUser (user );
136
- } catch (WebAuthnSampleBusinessException ex ) {
176
+ } catch (IllegalArgumentException ex ) {
137
177
return VIEW_SIGNUP_SIGNUP ;
138
178
}
139
179
0 commit comments