Skip to content

Commit 8808a2c

Browse files
committed
Add WebAuthn sample application
1 parent adb014e commit 8808a2c

19 files changed

+1463
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
plugins {
2+
id "io.spring.convention.spring-sample-boot"
3+
}
4+
5+
dependencies {
6+
compile project(':spring-security-webauthn')
7+
compile('org.springframework.boot:spring-boot-starter-web')
8+
9+
compile("org.slf4j:jcl-over-slf4j")
10+
compile('ch.qos.logback:logback-classic')
11+
compile('org.thymeleaf:thymeleaf-spring5')
12+
13+
compile('org.webjars:bootstrap:4.1.1')
14+
compile('org.webjars:jquery:3.3.1')
15+
compile('org.webjars:font-awesome:5.8.2')
16+
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.webauthn.sample;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
import org.springframework.context.annotation.ComponentScan;
22+
23+
/**
24+
* SampleWebApplication
25+
*/
26+
@SpringBootApplication
27+
@ComponentScan("org.springframework.security.webauthn.sample.app")
28+
@ComponentScan("org.springframework.security.webauthn.sample.domain")
29+
public class SampleWebApplication {
30+
31+
public static void main(String[] args) {
32+
SpringApplication.run(SampleWebApplication.class, args);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.webauthn.sample.app.config;
18+
19+
import com.webauthn4j.validator.WebAuthnAuthenticationContextValidator;
20+
import com.webauthn4j.validator.WebAuthnRegistrationContextValidator;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
24+
import org.springframework.security.core.userdetails.UserDetailsService;
25+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
26+
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;
34+
import org.springframework.security.webauthn.userdetails.WebAuthnUserDetailsService;
35+
36+
@Configuration
37+
public class WebSecurityBeanConfig {
38+
39+
@Bean
40+
public WebAuthnRegistrationRequestValidator webAuthnRegistrationRequestValidator(ServerPropertyProvider serverPropertyProvider) {
41+
WebAuthnRegistrationContextValidator webAuthnRegistrationContextValidator = WebAuthnRegistrationContextValidator.createNonStrictRegistrationContextValidator();
42+
return new WebAuthnRegistrationRequestValidator(webAuthnRegistrationContextValidator, serverPropertyProvider);
43+
}
44+
45+
@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);
50+
}
51+
52+
@Bean
53+
public WebAuthnAuthenticationContextValidator webAuthnAuthenticationContextValidator() {
54+
return new WebAuthnAuthenticationContextValidator();
55+
}
56+
57+
@Bean
58+
public PasswordEncoder passwordEncoder() {
59+
return new BCryptPasswordEncoder();
60+
}
61+
62+
// Not to register DaoAuthenticationProvider to ProviderManager,
63+
// initialize DaoAuthenticationProvider manually instead of using DaoAuthenticationConfigurer.
64+
@Bean
65+
public DaoAuthenticationProvider daoAuthenticationProvider(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
66+
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
67+
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
68+
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
69+
return daoAuthenticationProvider;
70+
}
71+
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.webauthn.sample.app.config;
18+
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;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.Configuration;
25+
import org.springframework.context.annotation.Import;
26+
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
27+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
28+
import org.springframework.security.config.annotation.authentication.configurers.mfa.MultiFactorAuthenticationProviderConfigurer;
29+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
30+
import org.springframework.security.config.annotation.web.builders.WebSecurity;
31+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
32+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
33+
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
34+
import org.springframework.security.webauthn.authenticator.WebAuthnAuthenticatorService;
35+
import org.springframework.security.webauthn.config.configurers.WebAuthnAuthenticationProviderConfigurer;
36+
import org.springframework.security.webauthn.userdetails.WebAuthnUserDetailsService;
37+
38+
import static org.springframework.security.webauthn.config.configurers.WebAuthnLoginConfigurer.webAuthnLogin;
39+
40+
41+
/**
42+
* Security Configuration
43+
*/
44+
@Configuration
45+
@Import(value = WebSecurityBeanConfig.class)
46+
@EnableWebSecurity
47+
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
48+
49+
@Autowired
50+
private DaoAuthenticationProvider daoAuthenticationProvider;
51+
52+
@Autowired
53+
private WebAuthnUserDetailsService userDetailsService;
54+
55+
@Autowired
56+
private WebAuthnAuthenticatorService authenticatorService;
57+
58+
@Autowired
59+
private WebAuthnAuthenticationContextValidator webAuthnAuthenticationContextValidator;
60+
61+
@Override
62+
public void configure(AuthenticationManagerBuilder builder) throws Exception {
63+
builder.apply(new WebAuthnAuthenticationProviderConfigurer<>(userDetailsService, authenticatorService, webAuthnAuthenticationContextValidator));
64+
builder.apply(new MultiFactorAuthenticationProviderConfigurer<>(daoAuthenticationProvider));
65+
}
66+
67+
@Override
68+
public void configure(WebSecurity web) {
69+
// ignore static resources
70+
web.ignoring().antMatchers(
71+
"/favicon.ico",
72+
"/webjars/**",
73+
"/js/**",
74+
"/css/**");
75+
}
76+
77+
/**
78+
* Configure SecurityFilterChain
79+
*/
80+
@Override
81+
protected void configure(HttpSecurity http) throws Exception {
82+
83+
// WebAuthn Login
84+
http.apply(webAuthnLogin())
85+
.rpName("Spring Security WebAuthn Sample")
86+
.attestation(AttestationConveyancePreference.NONE)
87+
.publicKeyCredParams()
88+
.addPublicKeyCredParams(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.RS256) // Windows Hello
89+
.addPublicKeyCredParams(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256) // FIDO U2F Key, etc
90+
.and()
91+
.loginPage("/login")
92+
.usernameParameter("username")
93+
.passwordParameter("password")
94+
.credentialIdParameter("credentialId")
95+
.clientDataJSONParameter("clientDataJSON")
96+
.authenticatorDataParameter("authenticatorData")
97+
.signatureParameter("signature")
98+
.clientExtensionsJSONParameter("clientExtensionsJSON")
99+
.loginProcessingUrl("/login")
100+
.defaultSuccessUrl("/dashboard");
101+
102+
// Logout
103+
http.logout()
104+
.logoutUrl("/logout");
105+
// Authorization
106+
http.authorizeRequests()
107+
.mvcMatchers("/").permitAll()
108+
.mvcMatchers("/signup").permitAll()
109+
.mvcMatchers("/login").permitAll()
110+
.mvcMatchers("/h2-console/**").denyAll()
111+
.anyRequest().fullyAuthenticated();
112+
113+
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
114+
115+
}
116+
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.webauthn.sample.app.web;
18+
19+
import com.webauthn4j.data.AuthenticatorTransport;
20+
21+
import javax.validation.Valid;
22+
import javax.validation.constraints.NotNull;
23+
import java.util.Set;
24+
25+
public class AuthenticatorCreateForm {
26+
27+
@NotNull
28+
@Valid
29+
private String clientDataJSON;
30+
31+
@NotNull
32+
@Valid
33+
private String attestationObject;
34+
35+
private Set<AuthenticatorTransport> transports;
36+
37+
@NotNull
38+
private String clientExtensions;
39+
40+
public String getClientDataJSON() {
41+
return clientDataJSON;
42+
}
43+
44+
public void setClientDataJSON(String clientDataJSON) {
45+
this.clientDataJSON = clientDataJSON;
46+
}
47+
48+
public String getAttestationObject() {
49+
return attestationObject;
50+
}
51+
52+
public void setAttestationObject(String attestationObject) {
53+
this.attestationObject = attestationObject;
54+
}
55+
56+
public Set<AuthenticatorTransport> getTransports() {
57+
return transports;
58+
}
59+
60+
public void setTransports(Set<AuthenticatorTransport> transports) {
61+
this.transports = transports;
62+
}
63+
64+
public String getClientExtensions() {
65+
return clientExtensions;
66+
}
67+
68+
public void setClientExtensions(String clientExtensions) {
69+
this.clientExtensions = clientExtensions;
70+
}
71+
}

0 commit comments

Comments
 (0)