Skip to content

Commit e7ba6bf

Browse files
committed
Redesign not to expose WebAuthn4J types
1 parent d7981e3 commit e7ba6bf

File tree

84 files changed

+2324
-4220
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+2324
-4220
lines changed

samples/javaconfig/webauthn/src/main/java/org/springframework/security/webauthn/sample/app/config/WebSecurityBeanConfig.java

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,60 @@
1616

1717
package org.springframework.security.webauthn.sample.app.config;
1818

19-
import com.webauthn4j.validator.WebAuthnAuthenticationContextValidator;
20-
import com.webauthn4j.validator.WebAuthnRegistrationContextValidator;
2119
import org.springframework.context.annotation.Bean;
2220
import org.springframework.context.annotation.Configuration;
2321
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
2422
import org.springframework.security.core.userdetails.UserDetailsService;
2523
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
2624
import org.springframework.security.crypto.password.PasswordEncoder;
27-
import org.springframework.security.webauthn.WebAuthnRegistrationRequestValidator;
28-
import org.springframework.security.webauthn.challenge.ChallengeRepository;
29-
import org.springframework.security.webauthn.challenge.HttpSessionChallengeRepository;
30-
import org.springframework.security.webauthn.options.OptionsProvider;
31-
import org.springframework.security.webauthn.options.OptionsProviderImpl;
32-
import org.springframework.security.webauthn.server.ServerPropertyProvider;
33-
import org.springframework.security.webauthn.server.ServerPropertyProviderImpl;
25+
import org.springframework.security.webauthn.*;
26+
import org.springframework.security.webauthn.challenge.HttpSessionWebAuthnChallengeRepository;
27+
import org.springframework.security.webauthn.challenge.WebAuthnChallengeRepository;
28+
import org.springframework.security.webauthn.server.EffectiveRpIdProvider;
29+
import org.springframework.security.webauthn.server.WebAuthnServerPropertyProvider;
30+
import org.springframework.security.webauthn.server.WebAuthnServerPropertyProviderImpl;
31+
import org.springframework.security.webauthn.userdetails.InMemoryWebAuthnAndPasswordUserDetailsManager;
3432
import org.springframework.security.webauthn.userdetails.WebAuthnUserDetailsService;
3533

3634
@Configuration
3735
public class WebSecurityBeanConfig {
3836

3937
@Bean
40-
public WebAuthnRegistrationRequestValidator webAuthnRegistrationRequestValidator(ServerPropertyProvider serverPropertyProvider) {
41-
WebAuthnRegistrationContextValidator webAuthnRegistrationContextValidator = WebAuthnRegistrationContextValidator.createNonStrictRegistrationContextValidator();
42-
return new WebAuthnRegistrationRequestValidator(webAuthnRegistrationContextValidator, serverPropertyProvider);
38+
public WebAuthnServerPropertyProvider webAuthnServerPropertyProvider(EffectiveRpIdProvider effectiveRpIdProvider, WebAuthnChallengeRepository challengeRepository){
39+
return new WebAuthnServerPropertyProviderImpl(effectiveRpIdProvider, challengeRepository);
4340
}
4441

4542
@Bean
46-
public ServerPropertyProvider serverPropertyProvider(WebAuthnUserDetailsService webAuthnUserDetailsService) {
47-
ChallengeRepository challengeRepository = new HttpSessionChallengeRepository();
48-
OptionsProvider optionsProvider = new OptionsProviderImpl(webAuthnUserDetailsService, challengeRepository);
49-
return new ServerPropertyProviderImpl(optionsProvider, challengeRepository);
43+
public WebAuthnChallengeRepository webAuthnChallengeRepository(){
44+
return new HttpSessionWebAuthnChallengeRepository();
5045
}
5146

5247
@Bean
53-
public WebAuthnAuthenticationContextValidator webAuthnAuthenticationContextValidator() {
54-
return new WebAuthnAuthenticationContextValidator();
48+
public InMemoryWebAuthnAndPasswordUserDetailsManager webAuthnUserDetailsService(){
49+
return new InMemoryWebAuthnAndPasswordUserDetailsManager();
5550
}
5651

52+
@Bean
53+
public WebAuthnOptionWebHelper webAuthnOptionWebHelper(WebAuthnChallengeRepository challengeRepository, WebAuthnUserDetailsService userDetailsService){
54+
return new WebAuthnOptionWebHelper(challengeRepository, userDetailsService);
55+
}
56+
57+
@Bean
58+
public WebAuthnManager webAuthnAuthenticationManager(){
59+
return new WebAuthn4JWebAuthnManager();
60+
}
61+
62+
@Bean
63+
public WebAuthnDataConverter webAuthnDataConverter(){
64+
return new WebAuthnDataConverter();
65+
}
66+
67+
@Bean
68+
public WebAuthnRegistrationRequestValidator webAuthnRegistrationRequestValidator(WebAuthnManager webAuthnManager, WebAuthnServerPropertyProvider webAuthnServerPropertyProvider){
69+
return new WebAuthnRegistrationRequestValidator(webAuthnManager, webAuthnServerPropertyProvider);
70+
}
71+
72+
5773
@Bean
5874
public PasswordEncoder passwordEncoder() {
5975
return new BCryptPasswordEncoder();

samples/javaconfig/webauthn/src/main/java/org/springframework/security/webauthn/sample/app/config/WebSecurityConfig.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
package org.springframework.security.webauthn.sample.app.config;
1818

19-
import com.webauthn4j.data.AttestationConveyancePreference;
20-
import com.webauthn4j.data.PublicKeyCredentialType;
21-
import com.webauthn4j.data.attestation.statement.COSEAlgorithmIdentifier;
22-
import com.webauthn4j.validator.WebAuthnAuthenticationContextValidator;
2319
import org.springframework.beans.factory.annotation.Autowired;
2420
import org.springframework.context.annotation.Configuration;
2521
import org.springframework.context.annotation.Import;
@@ -32,6 +28,7 @@
3228
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
3329
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
3430
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
31+
import org.springframework.security.webauthn.WebAuthnManager;
3532
import org.springframework.security.webauthn.authenticator.WebAuthnAuthenticatorService;
3633
import org.springframework.security.webauthn.config.configurers.WebAuthnAuthenticationProviderConfigurer;
3734
import org.springframework.security.webauthn.userdetails.WebAuthnUserDetailsService;
@@ -57,11 +54,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
5754
private WebAuthnAuthenticatorService authenticatorService;
5855

5956
@Autowired
60-
private WebAuthnAuthenticationContextValidator webAuthnAuthenticationContextValidator;
57+
private WebAuthnManager webAuthnManager;
6158

6259
@Override
6360
public void configure(AuthenticationManagerBuilder builder) throws Exception {
64-
builder.apply(new WebAuthnAuthenticationProviderConfigurer<>(userDetailsService, authenticatorService, webAuthnAuthenticationContextValidator));
61+
builder.apply(new WebAuthnAuthenticationProviderConfigurer<>(userDetailsService, authenticatorService, webAuthnManager));
6562
builder.apply(new MultiFactorAuthenticationProviderConfigurer<>(daoAuthenticationProvider));
6663
}
6764

@@ -83,12 +80,6 @@ protected void configure(HttpSecurity http) throws Exception {
8380

8481
// WebAuthn Login
8582
http.apply(webAuthnLogin())
86-
.rpName("Spring Security WebAuthn Sample")
87-
.attestation(AttestationConveyancePreference.NONE)
88-
.publicKeyCredParams()
89-
.addPublicKeyCredParams(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.RS256) // Windows Hello
90-
.addPublicKeyCredParams(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256) // FIDO U2F Key, etc
91-
.and()
9283
.loginPage("/login")
9384
.usernameParameter("username")
9485
.passwordParameter("password")
@@ -115,4 +106,5 @@ protected void configure(HttpSecurity http) throws Exception {
115106

116107
}
117108

109+
118110
}

samples/javaconfig/webauthn/src/main/java/org/springframework/security/webauthn/sample/app/web/AuthenticatorCreateForm.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.security.webauthn.sample.app.web;
1818

19-
import com.webauthn4j.data.AuthenticatorTransport;
20-
2119
import javax.validation.Valid;
2220
import javax.validation.constraints.NotNull;
2321
import java.util.Set;
@@ -32,7 +30,7 @@ public class AuthenticatorCreateForm {
3230
@Valid
3331
private String attestationObject;
3432

35-
private Set<AuthenticatorTransport> transports;
33+
private Set<String> transports;
3634

3735
@NotNull
3836
private String clientExtensions;
@@ -53,11 +51,11 @@ public void setAttestationObject(String attestationObject) {
5351
this.attestationObject = attestationObject;
5452
}
5553

56-
public Set<AuthenticatorTransport> getTransports() {
54+
public Set<String> getTransports() {
5755
return transports;
5856
}
5957

60-
public void setTransports(Set<AuthenticatorTransport> transports) {
58+
public void setTransports(Set<String> transports) {
6159
this.transports = transports;
6260
}
6361

samples/javaconfig/webauthn/src/main/java/org/springframework/security/webauthn/sample/app/web/WebAuthnSampleController.java

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,39 @@
1616

1717
package org.springframework.security.webauthn.sample.app.web;
1818

19+
import com.webauthn4j.util.Base64UrlUtil;
1920
import com.webauthn4j.util.UUIDUtil;
21+
import org.apache.commons.logging.Log;
22+
import org.apache.commons.logging.LogFactory;
2023
import org.springframework.beans.factory.annotation.Autowired;
2124
import org.springframework.security.authentication.MultiFactorAuthenticationToken;
2225
import org.springframework.security.core.Authentication;
26+
import org.springframework.security.core.GrantedAuthority;
2327
import org.springframework.security.core.context.SecurityContextHolder;
2428
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;
2632
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;
2737
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;
3240
import org.springframework.stereotype.Controller;
3341
import org.springframework.ui.Model;
3442
import org.springframework.util.Base64Utils;
3543
import org.springframework.validation.BindingResult;
3644
import org.springframework.web.bind.annotation.ModelAttribute;
3745
import org.springframework.web.bind.annotation.RequestMapping;
3846
import org.springframework.web.bind.annotation.RequestMethod;
39-
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
4047

4148
import javax.servlet.http.HttpServletRequest;
42-
import javax.servlet.http.HttpServletResponse;
4349
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;
4752

4853
/**
4954
* Login controller
@@ -52,6 +57,8 @@
5257
@Controller
5358
public class WebAuthnSampleController {
5459

60+
private final Log logger = LogFactory.getLog(getClass());
61+
5562
private static final String REDIRECT_LOGIN = "redirect:/login";
5663
private static final String REDIRECT_SIGNUP = "redirect:/signup";
5764

@@ -62,14 +69,26 @@ public class WebAuthnSampleController {
6269
private static final String VIEW_DASHBOARD_DASHBOARD = "dashboard/dashboard";
6370

6471
@Autowired
65-
private WebAuthnUserDetailsServiceImpl webAuthnUserDetailsService;
72+
private InMemoryWebAuthnAndPasswordUserDetailsManager webAuthnUserDetailsService;
6673

6774
@Autowired
6875
private WebAuthnRegistrationRequestValidator registrationRequestValidator;
6976

77+
@Autowired
78+
private WebAuthnOptionWebHelper webAuthnOptionWebHelper;
79+
80+
@Autowired
81+
private WebAuthnDataConverter webAuthnDataConverter;
82+
7083
@Autowired
7184
private PasswordEncoder passwordEncoder;
7285

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+
7392
@RequestMapping(value = "/")
7493
public String index(Model model) {
7594
return REDIRECT_SIGNUP;
@@ -91,49 +110,70 @@ public String template(Model model) {
91110
}
92111

93112
@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) {
95114

96115
if (result.hasErrors()) {
97116
return VIEW_SIGNUP_SIGNUP;
98117
}
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+
);
100126
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);
109131
return VIEW_SIGNUP_SIGNUP;
110132
}
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();
111139

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+
}
113154

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());
117163

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());
127164
authenticators.add(authenticator);
128165

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+
132173

133-
UserEntity user = destination;
134174
try {
135175
webAuthnUserDetailsService.createUser(user);
136-
} catch (WebAuthnSampleBusinessException ex) {
176+
} catch (IllegalArgumentException ex) {
137177
return VIEW_SIGNUP_SIGNUP;
138178
}
139179

0 commit comments

Comments
 (0)