Hello!
we’re currently trying to get a 2FA implemented with Spring Security working with the latest v25 betas. Our implementation is hugely based on this blog post: Implementierung von Zwei-Faktor-Authentifizierung (2FA) in einer Spring Boot Anwendung (german).
Basically the flow is as following (details in the blog post):
- Normal username/password auth → UsernamePasswordAuthenticationToken, empty authorities, “2FA_REQUIRED” in auth details
- 2FA-Filter which checks the requests if “2FA_REQUIRED” is inside the auth details → Redirects to /verify page
- After 2FA verification → Set authorities and redirect to requested page
We’re struggeling how to get the filter chain working together with the Vaadin Login Flow and the new VaadinSecurityConfigurer :
@Bean // Defines a bean to configure the security filter chain
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
// Configure form-based authentication
.formLogin(form -> form
.loginPage("/login") // Specify the custom login page URL
.loginProcessingUrl("/login") // URL to submit the username and password
.defaultSuccessUrl("/verify", true) // Redirect to OTP verification upon successful login
.permitAll() // Allow all users to access the login page
)
.authorizeHttpRequests(authorize -> authorize
// Require authentication for any other requests not previously specified
.anyRequest().authenticated()
)
// Register the custom authentication provider with Spring Security
.authenticationProvider(customAuthProvider)
// Add the TwoFactorAuthenticationFilter after the UsernamePasswordAuthenticationFilter in the filter chain
.addFilterAfter(twoFactorFilter, UsernamePasswordAuthenticationFilter.class);
// Build and return the configured SecurityFilterChain
return http.build();
}
Aditionally, is the LoginForm usable for our case? I think the /verify-Redirect may break the vaadin callback redirection to the requested page?
Currently our setup is as following:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.with(VaadinSecurityConfigurer.vaadin(), configurer -> {
configurer.anyRequest(AuthorizeHttpRequestsConfigurer.AuthorizedUrl::authenticated);
configurer.loginView(LoginView.class);
})
// Configure form-based authentication
.formLogin(form -> form
.successForwardUrl("/verify")
.loginPage("/login") // Specify the custom login page URL
.loginProcessingUrl("/login") // URL to submit the username and password
.defaultSuccessUrl("/verify", true) // Redirect to OTP verification upon successful login
.permitAll() // Allow all users to access the login page
)
// Register the custom authentication provider with Spring Security
.authenticationProvider(customAuthProvider)
// Add the TwoFactorAuthenticationFilter after the UsernamePasswordAuthenticationFilter in the filter chain
.addFilterAfter(twoFactorFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
Login seems to work in basic features but for the filter, we are struggeling to filter out which requests need to be redirected:
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("===");
System.out.println("Request URI: " + request.getRequestURI());
System.out.println("Framework internal request: " + requestUtil.isFrameworkInternalRequest(request));
System.out.println("Endpoint request: " + requestUtil.isEndpointRequest(request));
if (auth != null
&& "2FA_REQUIRED".equals(auth.getDetails())
&& !("/verify".equals(request.getRequestURI()))
&& !requestUtil.isFrameworkInternalRequest(request)
&& requestUtil.isEndpointRequest(request)
) {
response.sendRedirect("/verify");
return;
}
}
For example the login redirect to the dashboard after login is marked as
Request URI: /
Framework internal request: true
Endpoint request: false
In my thoughts it’s at least should be an endpoint request but no internal request?
DashboardView is
@PermitAll
@Route("")
@PageTitle("Dashboard")
@Menu(order = 0, icon = "vaadin:clipboard-check", title = "Dashboard")
public class DashboardView extends Main {
public DashboardView() {
add(new Text(("Dashboard")));
}
}
Any advices?