{"record":{"id":"ec16acbecef7a560","repo":"apereo/cas","slug":"failed-to-authenticate-code-ec16ac","errorCode":null,"errorMessage":"Failed to authenticate code ","messagePattern":"Failed to authenticate code ","errorType":"exception","errorClass":"FailedLoginException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/web/flow/GoogleAuthenticatorConfirmAccountRegistrationAction.java","lineNumber":59,"sourceCode":"        val requestParameters = requestContext.getRequestParameters();\n        val accountId = requestParameters.getRequired(OneTimeTokenAccountConfirmSelectionRegistrationAction.REQUEST_PARAMETER_ACCOUNT_ID, Long.class);\n        val validate = requestParameters.getBoolean(OneTimeTokenAccountSaveRegistrationAction.REQUEST_PARAMETER_VALIDATE);\n        val account = repository.get(accountId);\n        Objects.requireNonNull(account, \"Account cannot be null\");\n        if (BooleanUtils.isTrue(validate)) {\n            val token = requestParameters.getRequired(GoogleAuthenticatorSaveRegistrationAction.REQUEST_PARAMETER_TOKEN, String.class);\n            val authentication = WebUtils.getAuthentication(requestContext);\n            val principal = authentication.getPrincipal().getId();\n            LOGGER.debug(\"Validating account [{}] with token [{}] for principal [{}]\", accountId, token, principal);\n            val tokenCredential = new GoogleAuthenticatorTokenCredential(token, accountId);\n            val validatedToken = validator.validate(authentication, tokenCredential);\n            if (validatedToken != null) {\n                LOGGER.debug(\"Validated OTP token [{}] successfully for [{}]\", validatedToken, principal);\n                accountRegistrationVerified(requestContext, account);\n                return success();\n            }\n            LOGGER.warn(\"Authorization of OTP token [{}] has failed\", token);\n            throw new FailedLoginException(\"Failed to authenticate code \" + token);\n        }\n\n        if (!isAccountRegistrationVerified(requestContext, account)) {\n            LOGGER.warn(\"Account registration is not verified for [{}]\", account.getId());\n            throw new FailedLoginException(\"Unauthorized account registration attempt for id \" + account.getId());\n        }\n\n        accountRegistrationUnverified(requestContext, account);\n        return success();\n    }\n\n    protected void accountRegistrationVerified(final RequestContext requestContext, final OneTimeTokenAccount account) {\n        account.getProperties().add(ACCOUNT_PROPERTY_REGISTRATION_VERIFIED);\n        repository.update(account);\n    }\n\n    protected void accountRegistrationUnverified(final RequestContext requestContext, final OneTimeTokenAccount account) {\n        account.getProperties().remove(ACCOUNT_PROPERTY_REGISTRATION_VERIFIED);","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/web/flow/GoogleAuthenticatorConfirmAccountRegistrationAction.java#L41-L77","documentation":"GoogleAuthenticatorConfirmAccountRegistrationAction requires the user to submit a valid OTP during the confirm-registration webflow step to prove device possession. When validator.validate returns null (the token fails authorization) it throws FailedLoginException 'Failed to authenticate code <token>'. Registration of the GAuth account therefore cannot be confirmed.","triggerScenarios":"doExecuteInternal receives a token on the confirm-account-registration flow, calls validator.validate(token, authentication) and validatedToken is null — wrong code, reused code, clock drift, or account/secret mismatch — triggering the FailedLoginException before accountRegistrationVerified is called.","commonSituations":"User mistypes the code shown during registration; code comes from a different app entry; server clock skew; user retries an OTP already consumed by a prior validation.","solutions":["Re-enter a freshly generated OTP from the newly enrolled authenticator entry","Verify the QR scan produced the correct secret (re-register the account to get a new QR)","Check CAS server time synchronization (NTP)","Confirm the same account being verified is the one whose secret matches the code"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// client: only submit fresh 6-digit codes\nif (!/^\\d{6}$/.test(token)) throw new IllegalArgumentException(\"Enter the 6-digit code from the app\");","typeGuard":null,"tryCatchPattern":"try {\n    action.executeInternal(rc);\n} catch (FailedLoginException e) {\n    return error(\"Registration could not be confirmed: enter a new code from your authenticator\");\n}","preventionTips":["Have users confirm the QR belongs to the account being registered","Keep server time NTP-synced","Generate a fresh code after each failed confirmation attempt","Re-scan the QR if failures persist (secret mismatch)"],"tags":["gauth","otp","webflow","mfa"],"backgroundTag":"authentication-failed","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"}