Home > OS >  AuthenticationManager returning Bad Credentials
AuthenticationManager returning Bad Credentials

Time:09-07

I have this error and I don't know what it could be. I'm following a tutorial to do the User authentication, But when I try to do the authentication it throws a "Bad credentials" but the credentials are right. I'm using MongoDB.

From the tutorial I'm following with these steps it should work (other users confirmed that they did) but the only thing I did differently from the tutorial is in the "ClientEntity" class that already existed and I just implemented what I needed.

User return:

ClientEntity(id=63166ddbe3ea6c4fffd70818, clientName=Test, clientCpf=000.000.000-00, [email protected], clientPassword=2b598e4c0e79baf9dc9211ad303e7626, clientIsBlocked=false, clientBirthDate=1989-05-20, creditCards=[CreditCardEntity()], clientCategory=[ACAO, COMEDIA])

My request to signin, I'm logging in by email and password:

AccountCredentials: AccountCredentials([email protected], password=2b598e4c0e79baf9dc9211ad303e7626)

I know the problem is in that class in "authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(email, password));" but I don't know how to solve it, because the credentials are correct

My AuthService class:

@Service
public class AuthService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private ClientRepository repository;

    public ResponseEntity signin(AccountCredentials data) {
        try {
            var email = data.getEmail();
            var password = data.getPassword();
            log.info("METODO SIGNIN, AccountCredentials: "   data);

            
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(email, password));

            log.info("FOI AUTENTICADO!");

            var user = repository.findByClientEmail(email);

            log.info("O valor de User é: "   user);

            var tokenResponse = new Token();
            if (user != null) {
                tokenResponse = tokenProvider.createAccessToken(email, user.getRoles());
            } else {
                throw new UsernameNotFoundException("Email "   email   " not found!");
            }
            return ResponseEntity.ok(tokenResponse);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.out.println(e.getCause());
            System.out.println(e.getLocalizedMessage());
            throw new BadCredentialsException("Invalid email/password supplied!");
        }
    }

    public ResponseEntity refreshToken(String email, String refreshToken) {
        var user = repository.findByClientEmail(email);
        var tokenResponse = new Token();
        if (user != null) {
            tokenResponse = tokenProvider.refreshToken(refreshToken);
        } else {
            throw new UsernameNotFoundException("Email "   email   " not found!");
        }
        return ResponseEntity.ok(tokenResponse);
    }
}

My ClientEntity:

@Data
@Document(collection = "Client")
public class ClientEntity implements UserDetails, Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    private String id;
    private String clientName;
    private String clientCpf;
    private String clientEmail;
    private String clientPassword;
    private boolean clientIsBlocked = false;
    private LocalDate clientBirthDate;
    private List<CreditCardEntity> creditCards;
    private List<ClientCategoryEnum> clientCategory;

    private List<Permission> permissions;

    public List<String> getRoles() {
        List<String> roles = new ArrayList<>();
        for (Permission permission : permissions) {
            roles.add("USER");
            roles.add(permission.getDescription());
        }
        return roles;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.permissions;
    }

    @Override
    public String getPassword() {
        return this.clientPassword;
    }

    @Override
    public String getUsername() {
        return this.clientEmail;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

My Class Permission:

@Data
@Document(collection = "Roles")
public class Permission implements GrantedAuthority, Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    private Long id;
    private String description;

    @Override
    public String getAuthority() {
        return this.description;
    }

}

My SecurityConfig:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtTokenProvider tokenProvider;

    /*
    @Bean
    public UserDetailsService userDetailsService() {
        return super.userDetailsService();
    }
     */

    @Bean
    public PasswordEncoder passwordEncoder() {
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
        DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("pbkdf2", encoders);
        passwordEncoder.setDefaultPasswordEncoderForMatches(new Pbkdf2PasswordEncoder());
        return passwordEncoder;
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/auth/signin",
                        "/auth/refresh"
                ).permitAll()
                .antMatchers("/api/movie_search/**").authenticated()
                .and()
                .cors()
                .and()
                .apply(new JwtConfigurer(tokenProvider));
    }
}

My CLass TokenProvider:

@Service
public class JwtTokenProvider{
    @Value("${security.jwt.token.secret-key:secret}")
    private String secretKey = "secret";

    @Value("${security.jwt.token.expire-length:3600000}")
    private long validityInMilliseconds = 3600000; // 1h

    @Autowired
    private UserDetailsService userDetailsService;

    Algorithm algorithm = null;

    @PostConstruct
    protected void init() {
        secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
        algorithm = Algorithm.HMAC256(secretKey.getBytes());
    }

    public Token createAccessToken(String email, List<String> roles) {
        Date now = new Date();
        Date validity = new Date(now.getTime()   validityInMilliseconds);
        var accessToken = getAccessToken(email, roles, now, validity);
        var refreshToken = getRefreshToken(email, roles, now);

        return new Token(email, true, now, validity, accessToken, refreshToken);
    }

    public Token refreshToken(String refreshToken) {
        if (refreshToken.contains("Bearer ")) refreshToken =
                refreshToken.substring("Bearer ".length());

        JWTVerifier verifier = JWT.require(algorithm).build();
        DecodedJWT decodedJWT = verifier.verify(refreshToken);
        String email = decodedJWT.getSubject();
        List<String> roles = decodedJWT.getClaim("roles").asList(String.class);
        return createAccessToken(email, roles);
    }

    private String getAccessToken(String email, List<String> roles, Date now, Date validity) {
        String issuerUrl = ServletUriComponentsBuilder
                .fromCurrentContextPath().build().toUriString();
        return JWT.create()
                .withClaim("roles", roles)
                .withIssuedAt(now)
                .withExpiresAt(validity)
                .withSubject(email)
                .withIssuer(issuerUrl)
                .sign(algorithm)
                .strip();
    }

    private String getRefreshToken(String email,  List<String> roles, Date now) {
        Date validityRefreshToken = new Date(now.getTime()   (validityInMilliseconds * 3));
        return JWT.create()
                .withClaim("roles", roles)
                .withIssuedAt(now)
                .withExpiresAt(validityRefreshToken)
                .withSubject(email)
                .sign(algorithm)
                .strip();
    }

    public Authentication getAuthentication(String token) {
        DecodedJWT decodedJWT = decodedToken(token);
        UserDetails userDetails = this.userDetailsService
                .loadUserByUsername(decodedJWT.getSubject());
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    private DecodedJWT decodedToken(String token) {
        Algorithm alg = Algorithm.HMAC256(secretKey.getBytes());
        JWTVerifier verifier = JWT.require(alg).build();
        DecodedJWT decodedJWT = verifier.verify(token);
        return decodedJWT;
    }

    public String resolveToken(HttpServletRequest req) {
        String bearerToken = req.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring("Bearer ".length());
        }
        return null;
    }

    public boolean validateToken(String token) {
        DecodedJWT decodedJWT = decodedToken(token);
        try {
            if (decodedJWT.getExpiresAt().before(new Date())) {
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new InvalidJwtAuthenticationException("Expired or invalid JWT token!");
        }
    }
}

My ClientService:

@Service
public class ClientService implements UserDetailsService {

    private final ClientRepository clientRepository;

    private final ModelMapper modelMapper;


    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        var user = clientRepository.findByClientEmail(email);
       
        if (user != null) {
            return user;
        } else {
            throw new UsernameNotFoundException("Email "   email   " not found!");
        }
    }
}

In debug I came across this: UsernamePasswordAuthenticationToken Credentials=[PROTECTED], Authenticated=false, Details=null, Granted Authorities=[]]

CodePudding user response:

Short answer: Passwords stored in your DB must be encrypted by PasswordEncoder.

Long answer: You created a bean of type PasswordEncoder, so it is used by AuthenticationProvider when you call .authenticate() method of AuthenticationManager class.
As stated in java docs, DelegatingPasswordEncoder expects your passwords to be stored in DB in format like this:

{encoderId}encryptedPassword

You use pbkdf2 password encoder as a default encoder, so passwords in DB should look like this:

{pbkdf2}encryptedPassword

By the look of the returned ClientEntity, you haven't used the PasswordEncoder when you saved this entity to a DB, so the raw password is stored instead of an encrypted one.

The authentication flow should look like this:

  1. your app registers a user, stores his password in DB in encrypted form using PasswordEncoder bean;
  2. user passes his username (email or whatever is neccessary) and raw password to your app through login form of API endpoint;
  3. AuthenticationProvider retrieves encrypted password from the DB and compares it with the raw password the user has provided
  • Related