Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for One-Time Token Login #15114

Closed
marcusdacoregio opened this issue May 20, 2024 · 3 comments
Closed

Add Support for One-Time Token Login #15114

marcusdacoregio opened this issue May 20, 2024 · 3 comments
Assignees
Labels
in: core An issue in spring-security-core type: enhancement A general enhancement
Milestone

Comments

@marcusdacoregio
Copy link
Contributor

marcusdacoregio commented May 20, 2024

We should add support for one time token authentication, one common example is magic links sent in email or a text code to log a user in.

Note that this is not the same as #3046 since it is not in the scope of this ticket to support MFA.

@marcusdacoregio marcusdacoregio added in: core An issue in spring-security-core type: enhancement A general enhancement labels May 20, 2024
@marcusdacoregio marcusdacoregio self-assigned this May 20, 2024
@marcusdacoregio marcusdacoregio changed the title Add Support for Passwordless Authentication Add Support for Passwordless Authentication with OTP May 20, 2024
@CrazyParanoid
Copy link

Hi @marcusdacoregio ! I can share my implementation which I use in my projects. The security configuration looks like this:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, OneTimePasswordVerificationFilter oneTimePasswordVerificationFilter) throws Exception {
        return http.httpBasic(AbstractHttpConfigurer::disable)
                        .csrf(AbstractHttpConfigurer::disable)
                        .authorizeHttpRequests(auth -> auth
                                .requestMatchers("/login/otp/**", "/otp/**")
                                .permitAll()
                                .anyRequest()
                                .authenticated())
                        .oauth2Login(login -> login.successHandler(new OneTimePasswordAuthenticationHandler()))
                        .addFilterAfter(new DefaultOneTimePasswordPageGeneratingFilter(), DefaultLoginPageGeneratingFilter.class)
                        .addFilterBefore(oneTimePasswordVerificationFilter, AuthorizationFilter.class)
                        .build();

    }

    @Bean
    AuthenticationProvider oneTimePasswordAuthenticationProvider() {
        return new OneTimePasswordAuthenticationProvider(new DefaultOneTimePasswordAuthenticationVerifier());
    }

    @Bean
    OneTimePasswordVerificationFilter oneTimePasswordVerificationFilter(AuthenticationProvider oneTimePasswordAuthenticationProvider) {
        OneTimePasswordVerificationFilter filter = new OneTimePasswordVerificationFilter(oneTimePasswordAuthenticationProvider::authenticate);
        filter.setAuthenticationFailureHandler(new OneTimePasswordAuthenticationHandler());
        return filter;
    }
}

I have several components, first of all this is a filter for verifying otp code OneTimePasswordVerificationFilter. It interacts with OneTimePasswordAuthenticationProvider, which verifies the otp code:

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    SecurityContext securityContext = SecurityContextHolder.getContext();
    Authentication authentication = securityContext.getAuthentication();
    String code = request.getParameter(otpCodeParameter);

    OneTimePasswordAuthenticationToken authenticationToken = new OneTimePasswordAuthenticationToken(authentication, code);

    return this.getAuthenticationManager().authenticate(authenticationToken);
}

OneTimePasswordAuthenticationProvider performs verification using the OneTimePasswordAuthenticationVerifier component:

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    OneTimePasswordAuthenticationToken authenticationToken = (OneTimePasswordAuthenticationToken) authentication;
    try {
         oneTimePasswordAuthenticationVerifier.verify(authenticationToken);
    } catch (OAuth2AuthorizationException ex) {
         OAuth2Error oauth2Error = ex.getError();
         throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), ex);
     }

    return authenticationToken.getAuthentication();
}

Implementation OneTimePasswordAuthenticationVerifier of depends on the authenticator chosen, for example GoogleAuthenticator.

public interface OneTimePasswordAuthenticationVerifier {

	void verify(OneTimePasswordAuthenticationToken authenticationToken);

}

To generate the code entry page, use DefaultOneTimePasswordPageGeneratingFilter. OneTimePasswordAuthenticationHandler provides a redirect to the page for entering the otp code after successful oauth 2.0 authorization.
I'm currently using such an implementation as part of the ouath 2.0 client along with spring cloud gateway.

@evgeniycheban
Copy link
Contributor

Hi, @marcusdacoregio can I work on it?

@marcusdacoregio
Copy link
Contributor Author

Hi @evgeniycheban, this feature will require several design interactions with the team members since we have other features in the horizon that might be affected by it (MFA for example). Because of that I think it will be better if one of the maintainers work on it.

However, since you have an extensive knowledge of the code base and have contributed to a few core features, I'd love to hear how you see this feature implemented, either a high level idea or some code.

marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Aug 12, 2024
@marcusdacoregio marcusdacoregio changed the title Add Support for Passwordless Authentication with OTP Add Support for One-Time Token Login Aug 13, 2024
marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Aug 14, 2024
marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Aug 20, 2024
marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Aug 27, 2024
marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Aug 27, 2024
@marcusdacoregio marcusdacoregio added this to the 6.4.0-M4 milestone Aug 27, 2024
marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Sep 3, 2024
marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Sep 3, 2024
ngocnhan-tran1996 pushed a commit to ngocnhan-tran1996/spring-security-fork that referenced this issue Sep 18, 2024
ngocnhan-tran1996 pushed a commit to ngocnhan-tran1996/spring-security-fork that referenced this issue Sep 18, 2024
ngocnhan-tran1996 pushed a commit to ngocnhan-tran1996/spring-security-fork that referenced this issue Sep 18, 2024
ngocnhan-tran1996 pushed a commit to ngocnhan-tran1996/spring-security-fork that referenced this issue Sep 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core An issue in spring-security-core type: enhancement A general enhancement
Projects
Status: No status
Development

No branches or pull requests

3 participants