apereo/cas · error · FailedLoginException

Authentication failed with status:

Error message

Authentication failed with status: 

What it means

After a successful registry check, the handler calls client.verify(otp) against YubiCloud; if the response status is anything other than OK, it throws FailedLoginException("Authentication failed with status: " + status). This is the standard bad-OTP/verification-failure path of the YubiKey handler.

Solutions

  1. Have the user generate a fresh OTP by touching the YubiKey; do not replay previous OTPs.
  2. Verify cas.authn.yubikey[0].client-id and secret-key are correct YubiCloud API credentials.
  3. Check outbound connectivity to api.yubikey.com and NTP-synced system time to avoid signature errors.
  4. Inspect the logged status value to identify the specific YubiCloud failure and address it.

Example fix

// before
cas.authn.yubikey[0].client-id=change-me
// after
cas.authn.yubikey[0].client-id=12345
cas.authn.yubikey[0].secret-key=base64secret==
Defensive patterns

Strategy: try-catch

Validate before calling

// check OTP shape (device prefix length) before submit
if (otp == null || otp.length() < 32 || otp.length() > 48) throw new IllegalArgumentException("malformed OTP");

Try / catch

try {
    handler.authenticate(credential);
} catch (FailedLoginException e) {
    // read YubiCloud status from message, prompt user for a fresh OTP
}

Prevention

When it happens

Trigger: YubiCloud returns a non-OK status (BAD_OTP, REPLAYED_OTP, BAD_SIGNATURE, MISSING_PARAMETER, etc.) for a YubiKey OTP submitted by a registered user.

Common situations: User mistypes the OTP or reuses an old one (replayed); server clock/signature issues cause BAD_SIGNATURE; wrong client id/secret configured; OTP copied incompletely.

Understand the failure class

Related errors


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

Appendix: source

Thrown at support/cas-server-support-yubikey-core/src/main/java/org/apereo/cas/adaptors/yubikey/YubiKeyAuthenticationHandler.java:101

        val authentication = Objects.requireNonNull(WebUtils.getInProgressAuthentication(),
            "CAS has no reference to an authentication event to locate a principal");
        val principal = authentication.getPrincipal();
        val uid = principal.getId();
        val publicId = registry.getAccountValidator().getTokenPublicId(otp);
        if (!this.registry.isYubiKeyRegisteredFor(uid, publicId)) {
            LOGGER.debug("YubiKey public id [{}] is not registered for user [{}]", publicId, uid);
            throw new AccountNotFoundException("YubiKey id is not recognized in registry");
        }

        try {
            val response = this.client.verify(otp);
            val status = response.getStatus();
            if (status.compareTo(ResponseStatus.OK) == 0) {
                LOGGER.debug("YubiKey response status [{}] at [{}]", status, response.getTimestamp());
                return createHandlerResult(yubiKeyCredential, this.principalFactory.createPrincipal(uid));
            }
            throw new FailedLoginException("Authentication failed with status: " + status);
        } catch (final Throwable e) {
            LoggingUtils.error(LOGGER, e);
            throw new FailedLoginException("YubiKey validation failed: " + e.getMessage());
        }
    }
}

View on GitHub (pinned to e7288fc434)