{"record":{"id":"56e3515b319d282c","repo":"apereo/cas","slug":"authorization-of-otp-token-has-failed","errorCode":null,"errorMessage":"Authorization of OTP token [{}] has failed","messagePattern":"Authorization of OTP token \\[(.+?)\\] has failed","errorType":"exception","errorClass":"FailedLoginException","httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/GoogleAuthenticatorAuthenticationHandler.java","lineNumber":71,"sourceCode":"    @Override\n    public boolean supports(final Credential credential) {\n        return GoogleAuthenticatorTokenCredential.class.isAssignableFrom(credential.getClass());\n    }\n\n    @Override\n    protected AuthenticationHandlerExecutionResult doAuthentication(final Credential credential, final Service service) throws Throwable {\n        val tokenCredential = (GoogleAuthenticatorTokenCredential) credential;\n        val authentication = Objects.requireNonNull(WebUtils.getInProgressAuthentication());\n        Objects.requireNonNull(authentication, \"No authentication is available to determine the principal\");\n        val validatedToken = validator.validate(authentication, tokenCredential);\n        if (validatedToken != null) {\n            val principal = authentication.getPrincipal().getId();\n            LOGGER.debug(\"Validated OTP token [{}] successfully for [{}]\", validatedToken, principal);\n            validator.store(validatedToken);\n            LOGGER.debug(\"Creating authentication result and building principal for [{}]\", principal);\n            return createHandlerResult(tokenCredential, principalFactory.createPrincipal(principal));\n        }\n        LOGGER.warn(\"Authorization of OTP token [{}] has failed\", credential);\n        throw new FailedLoginException(\"Failed to authenticate code \" + credential);\n    }\n}\n","sourceCodeStart":53,"sourceCodeEnd":75,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/GoogleAuthenticatorAuthenticationHandler.java#L53-L75","documentation":"The GoogleAuthenticatorAuthenticationHandler authenticated the OTP token format but the token was not valid for the user's registered account, so the handler logs a warning and throws FailedLoginException. This is the normal rejection path for an incorrect, expired, or already-used one-time code during multifactor login.","triggerScenarios":"doAuthentication calls validator.validate() which returns null (token does not match any window/scratch code for the user's stored secret, was already used, or no account exists); the handler then logs 'Authorization of OTP token has failed' and throws.","commonSituations":"User mistypes or mistranscribes the 6-digit code from their authenticator app; device clock drift pushes the code outside the validation window; the same token was already consumed (one-time use enforcement); the user's account secret was re-registered but the client still shows old codes.","solutions":["Have the user enter a freshly generated code from their authenticator app","Check clock synchronization (NTP) on both the server and the user's device, and review cas.authn.mfa.gauth.core.* window settings","Verify the account record exists in the token repository for the authenticating principal (scratch codes/secret correct)","Clear any previously stored tokens if token reuse prevention is rejecting valid codes"],"exampleFix":"// before (server rejects all codes due to clock drift)\n// cas.authn.mfa.gauth.core.time-step-size=30, window unchanged\n// after: widen validation window in cas.properties\ncas.authn.mfa.gauth.core.window-size=5","handlingStrategy":"validation","validationCode":"// before relying on login succeeding, ensure an account exists for the user\nCollection<? extends OneTimeTokenAccount> accts = repository.get(username);\nboolean canAttempt = (accts != null && !accts.isEmpty());","typeGuard":null,"tryCatchPattern":"try {\n    handlerResult = handler.doAuthentication(credential, service, appContext);\n} catch (FailedLoginException e) {\n    // treat as invalid OTP: prompt user for a fresh code, do not lock immediately\n}","preventionTips":["Keep server clocks NTP-synchronized","Tune cas.authn.mfa.gauth.core window-size for realistic clock drift","Educate users to enter freshly generated codes","Monitor FailedLoginException rates to detect repository/secret corruption"],"tags":["otp","authentication","mfa","login-failure"],"backgroundTag":"authentication-required","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}