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.
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.
}
Then two simple DTO's such as;
}
and a login DTO;
}