Added 2FA
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
package com.example.security;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wraps the standard username/password check with a downgrade step: if the authenticating user
|
||||
* has two-factor authentication enabled, the returned token carries only {@link #PRE_AUTH_ROLE}
|
||||
* instead of their real authorities.
|
||||
* <p>
|
||||
* This is the enforcement mechanism for 2FA — a session holding only that role satisfies no
|
||||
* {@code @PermitAll}/{@code @RolesAllowed} check on real application views, no matter what the
|
||||
* navigation layer does. {@link TwoFactorNavigationGuard} is the UX layer on top: it notices the
|
||||
* downgraded role and routes the user to the code-entry view. {@link TwoFactorService#verify}
|
||||
* plus {@link TwoFactorService#completeAuthentication} is what upgrades the session once the
|
||||
* correct code is supplied.
|
||||
*/
|
||||
@Component
|
||||
class TwoFactorAwareAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
// Deliberately not in the Role enum: that enum models roles persisted per-user, while this
|
||||
// is a transient authentication state no user is ever granted.
|
||||
static final String PRE_AUTH_ROLE = "PRE_AUTH_2FA";
|
||||
static final String PRE_AUTH_AUTHORITY = "ROLE_" + PRE_AUTH_ROLE;
|
||||
|
||||
private final DaoAuthenticationProvider delegate;
|
||||
private final TwoFactorService twoFactorService;
|
||||
|
||||
TwoFactorAwareAuthenticationProvider(
|
||||
UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, TwoFactorService twoFactorService) {
|
||||
delegate = new DaoAuthenticationProvider(userDetailsService);
|
||||
delegate.setPasswordEncoder(passwordEncoder);
|
||||
this.twoFactorService = twoFactorService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
var fullyAuthenticated = delegate.authenticate(authentication);
|
||||
if (fullyAuthenticated == null || !twoFactorService.isTwoFactorEnabled(fullyAuthenticated.getName())) {
|
||||
return fullyAuthenticated;
|
||||
}
|
||||
return UsernamePasswordAuthenticationToken.authenticated(
|
||||
fullyAuthenticated.getPrincipal(), fullyAuthenticated.getCredentials(),
|
||||
List.of(new SimpleGrantedAuthority(PRE_AUTH_AUTHORITY)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return delegate.supports(authentication);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user