Password Validation Unable to Verify

50 Views Asked by At

I'm trying to create a simple login against my database to verify a user. For whatever reason Bcrypt.checkpw shows false when a true statement is expected. I am at a loss for words why this is the case. I have tried multiple things over the last 3 days. For example, with these logs I expect a true match but instead receive false. Everything was working up until I introduced the salt/hash.

Checking password: 9999999
Against hash: $2a$10$0ATPkyNqcXT8u3RTfoX7BuOzxuTvuNnVH5dgqjVc4agIKHglv7SAC
Match result: false
Provided username: UNIUSER
Provided plaintext password: 9999999
Password check failed. Stored Password Hash:
$2a$10$0ATPkyNqcXT8u3RTfoX7BuOzxuTvuNnVH5dgqjVc4agIKHglv7SAC

In my user model I have the following relevant sections;

public void hashAndSetPassword(String plaintextPassword) {
    int saltRounds = 10;
    this.passwordHash = BCrypt.hashpw(plaintextPassword, BCrypt.gensalt(saltRounds));
}

public boolean checkPassword(String password) {
    boolean isMatch = BCrypt.checkpw(password, passwordHash);
    System.out.println("Checking password: " + password);
    System.out.println("Against hash: " + passwordHash);
    System.out.println("Match result: " + isMatch);
    return isMatch;
}

In my UserController, I have the following.

@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

@Autowired
public UserController(UserService userService) {
    this.userService = userService;
}

@PostMapping("/create")
public ResponseEntity<Map<String, Object>> createUser(@RequestBody User user) {
    // Take the plaintext password from the user input
    String plaintextPassword = user.getPasswordHash(); // Use getPasswordHash, not getPassword

    // Hash the password and set it in the User object
    user.hashAndSetPassword(plaintextPassword);

    User savedUser = userService.saveUser(user);

    Map<String, Object> response = new HashMap<>();
    response.put("message", "User created with ID: " + savedUser.getId());
    response.put("id", savedUser.getId());

    return ResponseEntity.ok(response);
}

private String generateSecureToken() {
    SecureRandom random = new SecureRandom();
    byte[] bytes = new byte[64];

    random.nextBytes(bytes);
    return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}

@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(@RequestBody Map<String, String> credentials,
        HttpServletResponse response) {
    String username = credentials.get("username");
    String password = credentials.get("password");

    User user = userService.findByUsername(username);

    if (user != null) {
        String storedPasswordHash = user.getPasswordHash(); // Get the stored password hash from the database
        
        if (user.checkPassword(password)) { // Compare with hashed password
            String sessionToken = generateSecureToken();

            user.setSessionToken(sessionToken);

            userService.saveUser(user);

            Map<String, Object> responseMap = new HashMap<>();
            responseMap.put("message", "Login successful");
            responseMap.put("username", username);

            Cookie cookie = new Cookie("authToken", sessionToken);
            cookie.setMaxAge(3600);
            cookie.setPath("/"); // Site wide
            response.addCookie(cookie);

            return ResponseEntity.ok(responseMap);
        } else {
            Map<String, Object> responseMap = new HashMap<>();
            responseMap.put("message", "Login failed");
            System.out.println("Provided username: " + username);
            System.out.println("Provided plaintext password: " + password);
            System.out.println("Password check failed. Stored Password Hash: " + storedPasswordHash);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseMap);
        }
    } else {
        Map<String, Object> responseMap = new HashMap<>();
        responseMap.put("message", "User not found");
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseMap);
    }
}

I am using Java in the backend, AngularJS in the front. JBcrypt version 0.4 with maven and apache. Oracle Database for my database. Any insight on this matter would be very much appreciated. passwordHash is being stored as a VARCHAR2 (60) in the DB as I had it 255 before but heard 60 might help. Nothing though. Thanks.

1

There are 1 best solutions below

0
radriaansNES On

For whoever runs into this issue in the future, utilizing DTO's to facilitate the process solved the issue. Not quite sure the exact reason considering on face value everything was being carried correctly between front/back/DB but please see below for fix.

@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

@Autowired
public UserController(UserService userService) {
    this.userService = userService;
}

@PostMapping("/create")
public ResponseEntity<Map<String, Object>> createUser(@RequestBody RegistrationDTO registration) {
    User user = new User();
    user.setUsername(registration.getUsername());
    user.setFirstName(registration.getFirstName());
    user.setLastName(registration.getLastName());
    user.setTelephone(registration.getTelephone());
    user.setAddress(registration.getAddress());
    user.setCity(registration.getCity());
    user.setPostalCode(registration.getPostalCode());
    user.setCountry(registration.getCountry());

    user.hashAndSetPassword(registration.getPassword());

    User savedUser = userService.saveUser(user);

    Map<String, Object> response = new HashMap<>();
    response.put("message", "User created with ID: " + savedUser.getId());
    response.put("id", savedUser.getId());

    return ResponseEntity.ok(response);
}

private String generateSecureToken() {
    SecureRandom random = new SecureRandom();
    byte[] bytes = new byte[64];

    random.nextBytes(bytes);
    return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}

@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(@RequestBody LoginDTO loginDTO, HttpServletResponse response) {
    String username = loginDTO.getUsername();
    String password = loginDTO.getPassword();
    User user = userService.findByUsername(username);

    if (user != null) {

        if (user.checkPassword(password)) { 
            String sessionToken = generateSecureToken();

            user.setSessionToken(sessionToken);

            userService.saveUser(user);

            Map<String, Object> responseMap = new HashMap<>();
            responseMap.put("message", "Login successful");
            responseMap.put("username", username);

            Cookie cookie = new Cookie("authToken", sessionToken);
            cookie.setMaxAge(3600);
            cookie.setPath("/"); // Site wide
            response.addCookie(cookie);

            return ResponseEntity.ok(responseMap);
        } else {
            Map<String, Object> responseMap = new HashMap<>();
            responseMap.put("message", "Login failed");
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseMap);
        }
    } else {
        Map<String, Object> responseMap = new HashMap<>();
        responseMap.put("message", "User not found");
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseMap);
    }
}

}

Then two simple DTO's such as;

public class RegistrationDTO {

private String username;
private String password;
private String firstName;
private String lastName;
private String telephone;
private String address;
private String city;
private String postalCode;
private String country;

// Getters
public String getUsername() {
    return username;
}

public String getPassword() {
    return password;
}

public String getFirstName() {
    return firstName;
}

public String getLastName() {
    return lastName;
}

public String getTelephone() {
    return telephone;
}

public String getAddress() {
    return address;
}

public String getCity() {
    return city;
}

public String getPostalCode() {
    return postalCode;
}

public String getCountry() {
    return country;
}

// Setters
public void setUsername(String username) {
    this.username = username;
}

public void setPassword(String password) {
    this.password = password;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public void setTelephone(String telephone) {
    this.telephone = telephone;
}

public void setAddress(String address) {
    this.address = address;
}

public void setCity(String city) {
    this.city = city;
}

public void setPostalCode(String postalCode) {
    this.postalCode = postalCode;
}

public void setCountry(String country) {
    this.country = country;
}

}

and a login DTO;

public class LoginDTO {

private String username;
private String password;

// Getters
public String getUsername() {
    return username;
}

public String getPassword() {
    return password;
}

// Setters
public void setUsername(String username) {
    this.username = username;
}

public void setPassword(String password) {
    this.password = password;
}

}