Home > Enterprise >  Not able to log in , authentication failed in Spring Security with good credentials
Not able to log in , authentication failed in Spring Security with good credentials

Time:03-01

I have an application which was working with (email/password) login process. Now i have implemented LDAP auth. A user enter in the authenticate process, if the database doesn't know him, it comes in the ldap process, and if it works, then we create his account in the database. Next we receive the JWT token.

The problem is that if we retry to connect, new UsernamePasswordAuthenticationToken(email, password); is returning authenticated false. But credentials are OK, it should works...

I don't understand what is happening.. user is shown in the database with the password encrypted..


@Service
@RequiredArgsConstructor
public class OpenLdapAuthenticationProvider implements AuthenticationProvider {

    @Value("${spring.ldap.enabled}")
    private Boolean enableLDAP;

    @Autowired
    private LdapTemplate ldapTemplate;

    private final UserService userService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String email = authentication.getName();
        String password = authentication.getCredentials().toString();
        // This method returns authenticated false !
        Authentication authenticationWithDatabase = this.tryAuthWithDatabase(email, password);
        
        if (authenticationWithDatabase.isAuthenticated() == false) {
                if (enableLDAP == true) {

                boolean authenticationWithLdap = this.tryAuthWithLdap(email, password);
                if (authenticationWithLdap == true) {
                    UserDataDTO userDataDTO = this.retrieveUserInformationFromLDAP(email);
                    userDataDTO.setEmail(email);
                    userDataDTO.setPassword(password);
                    List<AppUserRole> userRoles = new ArrayList<>();
                    userRoles.add(AppUserRole.ROLE_DEV_VIEW);
                    userDataDTO.setAppUserRoles(userRoles);

                    boolean trySignup = userService.signup(userDataDTO);
                    if (trySignup == true) {
                        return this.tryAuthWithDatabase(email, password);
                    } else {
                        throw new InternalServerErrorException("Your account is not in database. Sign in with LDAP was OK, but registration failed. This should not happen.");
                    }

                } else {
                    throw new UnAuthorizedErrorException(
                            "The connection has been refused (by the database and ldap). Check your credentials.");
                }
            } else {
                throw new UnAuthorizedErrorException(
                        "The connection has been refused by the database and cannot be made by ldap because it has been disabled.");
            }
        } else {
            return authenticationWithDatabase;
        }

    }

    private Authentication tryAuthWithDatabase(String email, String password) {
        return new UsernamePasswordAuthenticationToken(email, password);
    }

    private boolean tryAuthWithLdap(String email, String password) {
        Filter filter = new EqualsFilter("mail", email); // mail = le champs dans l'arbre LDAP
        return ldapTemplate.authenticate(LdapUtils.emptyLdapName(), filter.encode(), password);
    }

    public UserDataDTO retrieveUserInformationFromLDAP(String email) {
        LdapQuery query = LdapQueryBuilder.query().where("objectClass").is("user").and("mail").is(email);
        return ldapTemplate.search(query,
                (AttributesMapper<UserDataDTO>) attributes -> 
                UserDataDTO.builder()
                        .name(attributes.get("givenName").get().toString())
                        .surname(attributes.get("sn").get().toString())
                        .username(attributes.get("displayName").get().toString())
                        .team(attributes.get("department").get().toString())
                    .build()).get(0);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  private JwtTokenProvider jwtTokenProvider;

  @Autowired
  private OpenLdapAuthenticationProvider openLdapAuthenticationProvider;

  public WebSecurityConfig(JwtTokenProvider jwtTokenProvider) {
    this.jwtTokenProvider = jwtTokenProvider;
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(this.openLdapAuthenticationProvider);
  }


  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.addFilterBefore(corsFilter(), ChannelProcessingFilter.class);
    http.csrf().disable();
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    http.authorizeRequests()
        .antMatchers("/api/v3/authentificate").permitAll()
        .antMatchers("/api/v3/signup").permitAll()
        .anyRequest().authenticated();
    http.apply(new JwtTokenFilterConfigurer(jwtTokenProvider));
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    // Allow swagger to be accessed without authentication
    web.ignoring().antMatchers("/v3/api-docs/**")//
        .antMatchers("/swagger-resources/**")//
        .antMatchers("/swagger-ui/**")
        .antMatchers("/swagger-ui.html");
  }

  @Override
  @Bean
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

}
@Configuration
public class GenericBeanConfig {
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }

    @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }
    
}

Thanks for any help, i really don't understand what's happening..

CodePudding user response:

If you check UsernamePasswordAuthenticationToken.class, you will see 2 constructors:

    /**
     * This constructor can be safely used by any code that wishes to create a
     * <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()}
     * will return <code>false</code>.
     *
     */
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        setAuthenticated(false);
    }

    /**
     * This constructor should only be used by <code>AuthenticationManager</code> or
     * <code>AuthenticationProvider</code> implementations that are satisfied with
     * producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
     * authentication token.
     * @param principal
     * @param credentials
     * @param authorities
     */
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
            Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true); // must use super, as we override
    }

The same can be seen in its documentation

Spring security's default implementation expects you to provide at least one granted authority to your user instance

That happens because Authentication provider receives an Authentication object not authenticated as input only username and password, by applying its logic (fetching from database, etc) if the result matches it returns an Authentication object username, password and its authorities if it doesnt match it returns that same input object

To solve your problem, you can try the following purely illustrative code:

       // omitted code above

    private Authentication tryAuthWithDatabase(String email, String password) {
        // call your database or repository or service
        // var fetchData receives the object from database
        // perform token the matching against the fetch data

        if( resultMatches ) {
              return new UsernamePasswordAuthenticationToken(email, password, fetchData.getAuthorities() );
        }
        else {
             return new UsernamePasswordAuthenticationToken( email, password );
        }

    }

        // omitted code bellow

I hope i´ve helped. Let me know if you made it. Cheers!

  • Related