package demo;

import java.time.Duration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authorization.AuthorizationManagerFactories;
import org.springframework.security.authorization.AuthorizationManagerFactory;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authorization.EnableMultiFactorAuthentication;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;

/**
 * Variant 2 (step-up): everyone logs in with a password; only /admin/** additionally demands a
 * one-time token that was issued in the last five minutes.
 *
 * Profile "stepup" is the working setup. Profile "stepupbare" is the SAME filter chain without the
 * nested annotation below, to show what goes wrong.
 */
@Configuration
@EnableWebSecurity
@Profile({"stepup", "stepupbare"})
public class MfaStepUpConfig {

    @Bean
    SecurityFilterChain chain(HttpSecurity http, MfaTokenCatcher catcher) throws Exception {
        AuthorizationManagerFactory<RequestAuthorizationContext> adminFactors =
            AuthorizationManagerFactories.<RequestAuthorizationContext>multiFactor()
                .requireFactor(f -> f.passwordAuthority())
                .requireFactor(f -> f.ottAuthority().validDuration(Duration.ofMinutes(5)))
                .build();

        return http
            .authorizeHttpRequests(a -> a
                .requestMatchers("/").permitAll()
                .requestMatchers("/admin/**").access(adminFactors.hasRole("ADMIN"))
                .anyRequest().authenticated())
            .formLogin(Customizer.withDefaults())
            .oneTimeTokenLogin(o -> o.tokenGenerationSuccessHandler(catcher))
            .build();
    }

    /**
     * No factors are required globally (empty list). The annotation is here for its side effect:
     * it switches the login filters into "MFA mode", so a second login ADDS its factor to the
     * factors already on the session instead of replacing them.
     */
    @Configuration
    @Profile("stepup")
    @EnableMultiFactorAuthentication(authorities = {})
    static class FactorCarryForward {
    }
}
