Spring Security Custom Authentication Provider

In this article, we will get an understanding of Spring Security custom authentication provider. There are many use cases where the standard Authentication provider will not fulfill our need and we may need to create custom authentication provider for our Spring security application to get additional flexibility.

Advertisements

Spring Security Custom Authentication Provider

Before we get in to the details, it’s important to understand how the authentication workflow in Spring security. Let’s look at the following picture for better understanding.

DaoAuthenticationProvider
Spring Security Authentication Provider
  1. ProviderManager will handle all the authentication requests.
  2. The ProviderManager delegates the authentication to the list of configured AuthenitcationProvider(s).
  3. Every AuthenticationProvider specific the type of authentication/ request it can handle.

All Authentication requests will be processed by the AuthenticationProviders and it will return an Authentication object to the successful authentication else the provider will throw an exception.The DaoAuthenticationProvider is the most common implementation is the Spring security. This provider fetches the user details with the help of UserDetailsService.

The authentication flow works really well for many use cases unless we need better control of the process or may need some additional information during the authentication process. Let’s take a real world example where I had to auto-login the user in our eCommerce store, if they are coming from our other apps. Here was the high level workflow.

  1. Customer open the app in their mobile / browser.
  2. They do the login using the app credential.
  3. Now customer wants to buy something in our eCommerce store but we don’t want to force customer to have 2 different accounts and like to give a seamless login experience to the customer.

Here, the default authentication provider will not help us and we need Spring Security custom authentication provider to help us. In our case application was using Cognito but it could be any other external third party system.Let’s see how to define and configure a custom Authentication Provider.

1. Custom Authentication Provider

For our Spring Security custom authentication provider, we need to implement the AuthenticationProvider interface. This is how our Authentication Provider will look like:

Advertisements
Advertisements
package com.javadevjournal.core.security.authentication.provider;


import com.javadevjournal.core.security.authentication.ExternalServiceAuthenticationToken;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import javax.annotation.Resource;

/**
 * <p>A custom Authentication provider example. To create custom AuthenticationProvider, we need to implement the
 * AuthenticationProvider provide the implementation for the authenticate and support method.</p>
 */
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Resource
    UserDetailsService userDetailsService;

    /**
     * <p> The authenticate method to authenticate the request. We will get the username from the Authentication object and will
     * use the custom @userDetailsService service to load the given user.</p>
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        final String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
        if (StringUtils.isEmpty(username)) {
            throw new BadCredentialsException("invalid login details");
        }
        // get user details using Spring security user details service
        UserDetails user = null;
        try {
            user = userDetailsService.loadUserByUsername(username);

        } catch (UsernameNotFoundException exception) {
            throw new BadCredentialsException("invalid login details");
        }
        return createSuccessfulAuthentication(authentication, user);
    }

    private Authentication createSuccessfulAuthentication(final Authentication authentication, final UserDetails user) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), authentication.getCredentials(), user.getAuthorities());
        token.setDetails(authentication.getDetails());
        return token;
    }

    @Override
    public boolean supports(Class << ? > authentication) {
        return authentication.equals(ExternalServiceAuthenticationToken.class);
    }
}

2. Register Custom Authentication Provider

The next step is to register our Spring Security custom authentication provider. We will use the Java configure for this. We can do this in the custom spring security class extending the WebSecurityConfigurerAdapter. There can be 2 options to configure the custom Authentication Provider with Spring Security.

  1. Use the custom authentication provider for all login requirements.
  2. Configure and use the custom authentication provider only for external authentication.

Let’s look at the first approach.

@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
    }

    /**
     * Custom authentication provider. This authentication provider will authenticate the user with the help of
     * @UserdetailsService.
     * @return
     */
    @Bean
    public CustomAuthenticationProvider authProvider() {
        CustomAuthenticationProvider authenticationProvider = new CustomAuthenticationProvider();
        return authenticationProvider;
    }

    /**
     * Authentication manager which will be invoked by Spring security filter chain. This authentication
     * manager will delegate the work to the Authentication provider to
     * authenticate the user. Look out for the Custom authenitcation provider in the above section to see
     * how it works with this.
     * @param auth
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(authProvider());
    }
}

3. Testing Application

Our custom authentication provider is now ready to accept the authentication request. Here is a sample authentication request processed by our custom Spring Security Authentication Provider.

Spring Security Custom Authentication Provider

This is a simple example of the Spring Security custom authentication provider. Also, our custom authentication provider is not validating the credentials. You have to either customize it to validate credentials or we should use this kind of workflow with the trusted application and should have other validations in place.

Summary

Spring security provides a lot of AuthenticationProviders which work in most of the cases, however, there are certain cases where we may need more flexibility and control with the providers. In this article we got an understanding of Spring Security custom authentication provider. We saw how to create and configure custom authentication provider for our application. The source code for this application is available on our GitHub Repository.

Advertisements