diff --git a/core/src/main/java/de/bstly/we/security/LocalAuthenticationProvider.java b/core/src/main/java/de/bstly/we/security/LocalAuthenticationProvider.java index 0f2338d..c0c7380 100755 --- a/core/src/main/java/de/bstly/we/security/LocalAuthenticationProvider.java +++ b/core/src/main/java/de/bstly/we/security/LocalAuthenticationProvider.java @@ -86,8 +86,7 @@ public class LocalAuthenticationProvider extends DaoAuthenticationProvider imple } else { for (AdditionalAuthenticationProvider provider : providers) { if (provider.supports(auth.getClass())) { - auth = provider.authenticate(auth); - return this.secondFactorCheck(auth); + return provider.authenticate(auth); } } } diff --git a/pom.xml b/pom.xml index 620d894..4cc0b91 100755 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 2.2 1.14.1 3.6.3 - 4.0.0 + 4.0.1 diff --git a/webauthn/src/main/java/de/bstly/we/webauthn/businesslogic/WebAuthnManager.java b/webauthn/src/main/java/de/bstly/we/webauthn/businesslogic/WebAuthnManager.java index 7164d87..458dc5a 100644 --- a/webauthn/src/main/java/de/bstly/we/webauthn/businesslogic/WebAuthnManager.java +++ b/webauthn/src/main/java/de/bstly/we/webauthn/businesslogic/WebAuthnManager.java @@ -253,12 +253,6 @@ public class WebAuthnManager implements UserDataProvider { } } - /** - * Creates a WebAuthn login request that does not depend on the username. - * - * This avoids username enumeration and allows resolving the user from the - * returned credentialId during the finish step. - */ public String createLoginRequest() { try { StartAssertionOptions.StartAssertionOptionsBuilder optionsBuilder = StartAssertionOptions.builder() @@ -281,21 +275,14 @@ public class WebAuthnManager implements UserDataProvider { } } - /** - * Validates a WebAuthn login assertion where the user is resolved from the - * credentialId contained in the assertion. - * - * @return the authenticated userId if successful, otherwise empty. - */ public Optional validateLoginRequest(String assertionJson) { try { JsonNode assertionNode = objectMapper.readTree(assertionJson); PublicKeyCredential pkc = PublicKeyCredential .parseAssertionResponseJson(assertionJson); - - ByteArray credentialId = pkc.getId(); - WebAuthnCredential credential = credentialRepository.findByCredentialId(credentialId.getBase64Url()); - if (credential == null || !credential.isEnabled() || credential.getUsage() != WebAuthnUsage.LOGIN) { + WebAuthnCredential credential = credentialRepository.findByCredentialId(pkc.getId().getBase64Url()); + if (credential == null || !credential.isEnabled() || credential.getUsage() != WebAuthnUsage.LOGIN + && credential.getUsage() != WebAuthnUsage.LOGIN_2FA) { return Optional.empty(); } @@ -337,12 +324,9 @@ public class WebAuthnManager implements UserDataProvider { PublicKeyCredential pkc = PublicKeyCredential .parseAssertionResponseJson(assertionJson); + WebAuthnCredential credential = credentialRepository.findByCredentialId(pkc.getId().getBase64Url()); - ByteArray credentialId = pkc.getId(); - WebAuthnCredential credential = credentialRepository.findByCredentialId(credentialId.getBase64Url()); - - if (credential == null || !credential.isEnabled() - || (credential.getUsage() != usage)) { + if (credential == null || !credential.isEnabled() || (credential.getUsage() != usage)) { return false; } @@ -382,6 +366,16 @@ public class WebAuthnManager implements UserDataProvider { return false; } + public WebAuthnCredential getCredentialForAssertionJson(String assertionJson) { + try { + PublicKeyCredential pkc = PublicKeyCredential + .parseAssertionResponseJson(assertionJson); + return credentialRepository.findByCredentialId(pkc.getId().getBase64Url()); + } catch (Exception e) { + return null; + } + } + public ByteArray getUserHandle(Long userId) { WebAuthnUserHandle webAuthnUserHandle = webAuthnUserHandleRepository.findByTarget(userId); diff --git a/webauthn/src/main/java/de/bstly/we/webauthn/model/WebAuthnUsage.java b/webauthn/src/main/java/de/bstly/we/webauthn/model/WebAuthnUsage.java index 853f97f..b854a71 100644 --- a/webauthn/src/main/java/de/bstly/we/webauthn/model/WebAuthnUsage.java +++ b/webauthn/src/main/java/de/bstly/we/webauthn/model/WebAuthnUsage.java @@ -1,5 +1,5 @@ package de.bstly.we.webauthn.model; public enum WebAuthnUsage { - NONE, TWO_FA, LOGIN + NONE, TWO_FA, LOGIN, LOGIN_2FA } diff --git a/webauthn/src/main/java/de/bstly/we/webauthn/security/WebAuthnAuthenticationProvider.java b/webauthn/src/main/java/de/bstly/we/webauthn/security/WebAuthnAuthenticationProvider.java index 67d3881..fdc02b3 100644 --- a/webauthn/src/main/java/de/bstly/we/webauthn/security/WebAuthnAuthenticationProvider.java +++ b/webauthn/src/main/java/de/bstly/we/webauthn/security/WebAuthnAuthenticationProvider.java @@ -8,14 +8,19 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.stereotype.Component; import de.bstly.we.businesslogic.UserManager; import de.bstly.we.model.User; import de.bstly.we.security.LocalUserDetailsService; import de.bstly.we.security.businesslogic.AdditionalAuthenticationProvider; +import de.bstly.we.security.businesslogic.SecondFactorProviderManager; import de.bstly.we.webauthn.businesslogic.WebAuthnManager; +import de.bstly.we.webauthn.model.WebAuthnCredential; +import de.bstly.we.webauthn.model.WebAuthnUsage; @Component public class WebAuthnAuthenticationProvider implements AdditionalAuthenticationProvider { @@ -26,6 +31,8 @@ public class WebAuthnAuthenticationProvider implements AdditionalAuthenticationP private LocalUserDetailsService userDetailsService; @Autowired private WebAuthnManager webAuthnManager; + @Autowired + private SecondFactorProviderManager secondFactorProviderManager; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { @@ -48,6 +55,26 @@ public class WebAuthnAuthenticationProvider implements AdditionalAuthenticationP UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername()); List authorities = List.copyOf(userDetails.getAuthorities()); + + WebAuthnCredential credential = webAuthnManager.getCredentialForAssertionJson(assertionJson); + + if (credential == null) { + throw new BadCredentialsException("User not authenticated"); + } + + if (credential.getUsage() != WebAuthnUsage.LOGIN + && credential.getUsage() != WebAuthnUsage.LOGIN_2FA) { + throw new BadCredentialsException("User not authenticated"); + } + + if (credential.getUsage() != WebAuthnUsage.LOGIN_2FA + && !secondFactorProviderManager.getEnabled(userId).isEmpty()) { + PreAuthenticatedAuthenticationToken newAuth = new PreAuthenticatedAuthenticationToken(userDetails, "", + AuthorityUtils.createAuthorityList("ROLE_PRE_AUTH_USER")); + newAuth.setAuthenticated(false); + return newAuth; + } + return new UsernamePasswordAuthenticationToken(userDetails, assertionJson, authorities); }