apereo/cas · error · FailedLoginException
Password does not match value on record.
Error message
Password does not match value on record.
What it means
QueryDatabaseAuthenticationHandler throws FailedLoginException('Password does not match value on record.') when the configured SQL returns a password column but the submitted password does not match it. If an 'original' (already-encoded) password property is set, that is compared via matches(); otherwise the raw password is compared case-insensitively against the stored value.
Solutions
- Verify fieldPassword points at the real password column and that the storage format matches the comparison mode (set credentialToPasswordEncoder/credentialEncodingAlgorithm for hashed storage)
- Check the sql only returns rows for the correct user so dbPassword is the right record
- Trim/normalize stored values (no trailing spaces) or fix charset configuration
- Test with a known-good user/password pair to isolate encoder vs data problems
Example fix
// before: DB stores SHA-256 hex, handler compares plaintext // cas.authn.jdbc.query[0].fieldPassword=password // after // cas.authn.jdbc.query[0].fieldPassword=password // cas.authn.jdbc.query[0].credentialEncodingAlgorithm=SHA-256
Defensive patterns
Strategy: validation
Validate before calling
Map<String,Object> row = jdbc.queryForMap(sql, user);
if (!row.containsKey("password")) throw new IllegalStateException("fieldPassword column missing in result");
String stored = (String) row.get("password");
// verify comparison mode matches storage format
boolean ok = stored.equals(digest(rawPassword, salt)) || stored.equalsIgnoreCase(rawPassword); Type guard
boolean isPlausiblePassword(Object v) { return v instanceof String s && !s.isBlank(); } Try / catch
try {
authResult = handler.authenticate(credential);
} catch (FailedLoginException e) {
if (e.getMessage().contains("Password does not match")) {
audit.logBadPassword(user); // wrong credentials; throttle and return generic error
throw new BadCredentialsException("Invalid credentials");
}
throw e;
} Prevention
- Match credentialEncodingAlgorithm/credentialToPasswordEncoder to the stored hash format
- Ensure the sql returns exactly one row per user with the password column
- Normalize stored values (no whitespace, consistent case/charset)
- Smoke-test one known-good credential per environment
When it happens
Trigger: dbFields contains properties.getFieldPassword() but either (a) originalPassword is non-blank and matches(originalPassword, dbPassword) is false, or (b) originalPassword is blank and the credential password is not case-insensitively equal to the stored value.
Common situations: Password stored hashed but compared as plaintext (or vice versa), wrong fieldPassword column configured, stored value includes whitespace/salt prefix, casing mismatch when DB stores mixed case and CI comparison still fails due to different algorithm output.
Related errors
- [e.getMessage()]
- Password does not match value on record.
- Password has expired
- [username] not found with SQL query.
- Failed to authenticate user
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/972ba5620c3b2f18.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-jdbc-authentication/src/main/java/org/apereo/cas/jdbc/QueryDatabaseAuthenticationHandler.java:63
+ ", (especially if the query results do not contain the password field),"
+ "and will instead only rely on a successful query execution with returned results in order to verify credentials");
}
}
@Override
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
final UsernamePasswordCredential credential, final String originalPassword) throws Throwable {
val username = credential.getUsername();
val password = credential.toPassword();
try {
val dbFields = query(credential);
if (dbFields.containsKey(properties.getFieldPassword())) {
val dbPassword = (String) dbFields.get(properties.getFieldPassword());
val originalPasswordMatchFails = StringUtils.isNotBlank(originalPassword) && !matches(originalPassword, dbPassword);
val originalPasswordEquals = StringUtils.isBlank(originalPassword) && !Strings.CI.equals(password, dbPassword);
if (originalPasswordMatchFails || originalPasswordEquals) {
throw new FailedLoginException("Password does not match value on record.");
}
} else {
LOGGER.debug("Password field is not found in the query results. Checking for result count...");
if (!dbFields.containsKey("total")) {
throw new FailedLoginException("Missing field 'total' from the query results for " + username);
}
val count = dbFields.get("total");
if (count == null || !NumberUtils.isCreatable(count.toString())) {
throw new FailedLoginException("Missing field value 'total' from the query results for "
+ username + " or value not parseable as a number");
}
val number = NumberUtils.createNumber(count.toString());
if (number.longValue() != 1) {
throw new FailedLoginException("No records found for user " + username);
}
}View on GitHub (pinned to e7288fc434)