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

  1. Verify fieldPassword points at the real password column and that the storage format matches the comparison mode (set credentialToPasswordEncoder/credentialEncodingAlgorithm for hashed storage)
  2. Check the sql only returns rows for the correct user so dbPassword is the right record
  3. Trim/normalize stored values (no trailing spaces) or fix charset configuration
  4. 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

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


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)