apereo/cas · warning

When the password field is left undefined, CAS will skip…

Error message

When the password field is left undefined, CAS will skip comparing database and user passwords for equality , (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

What it means

This constructor-time WARN in QueryDatabaseAuthenticationHandler fires when QueryJdbcAuthenticationProperties.fieldPassword is blank. Without a password field, CAS cannot compare the submitted password against a database column; authentication succeeds merely because the query returned at least one row, effectively turning the handler into a username-existence check. CAS warns because this silently weakens credential verification.

Solutions

  1. Set cas.authn.jdbc.query[].fieldPassword to the alias of the password column returned by the sql statement so CAS can compare hashes/passwords
  2. Ensure the sql SELECT actually includes the password field referenced by fieldPassword
  3. Configure the matching passwordEncoder/passwordPolicyConfiguration so stored hashes compare correctly
  4. If existence-only auth is truly intended, accept/audit the risk and document it; consider a stronger mechanism

Example fix

// before
cas.authn.jdbc.query[0].sql=SELECT username FROM users WHERE username=?
// after
cas.authn.jdbc.query[0].sql=SELECT username, password FROM users WHERE username=?
cas.authn.jdbc.query[0].fieldPassword=password
Defensive patterns

Strategy: validation

Validate before calling

if (casProperties.getAuthn().getJdbc().getQuery().stream().anyMatch(q -> q.getFieldPassword().isBlank())) { throw new IllegalStateException("query jdbc authn requires fieldPassword"); }

Prevention

When it happens

Trigger: Configuring cas.authn.jdbc.query[] without setting fieldPassword while still expecting password comparison; fieldPassword typo'd or named differently from the SELECT alias; deliberately using the handler as an existence check but forgetting the warning is expected.

Common situations: Copy-pasted query config from an example lacking fieldPassword; SELECT that omits the password column so fieldPassword was removed to stop errors; deployments intending bind-style or existence-based auth who did not realize the security implication.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/4d0cd39396952624. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-jdbc-authentication/src/main/java/org/apereo/cas/jdbc/QueryDatabaseAuthenticationHandler.java:44

 * password provided by the user. If they match, then authentication succeeds.
 * Default password translator is plaintext translator.
 *
 * @author Scott Battaglia
 * @author Dmitriy Kopylenko
 * @author Marvin S. Addison
 * @since 3.0.0
 */
@Slf4j
@Monitorable
public class QueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler<QueryJdbcAuthenticationProperties> {

    public QueryDatabaseAuthenticationHandler(final QueryJdbcAuthenticationProperties properties,

                                              final PrincipalFactory principalFactory,
                                              final DataSource dataSource) {
        super(properties, principalFactory, dataSource);
        if (StringUtils.isBlank(properties.getFieldPassword())) {
            LOGGER.warn("When the password field is left undefined, CAS will skip comparing database and user passwords for equality "
                + ", (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) {

View on GitHub (pinned to e7288fc434)