{"record":{"id":"67a895636c28554d","repo":"apereo/cas","slug":"account-registration-is-not-verified-for","errorCode":null,"errorMessage":"Account registration is not verified for [{}]","messagePattern":"Account registration is not verified for \\[(.+?)\\]","errorType":"exception","errorClass":"FailedLoginException","httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/web/flow/GoogleAuthenticatorConfirmAccountRegistrationAction.java","lineNumber":63,"sourceCode":"        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);\n        repository.update(account);\n    }\n\n    protected boolean isAccountRegistrationVerified(final RequestContext requestContext, final OneTimeTokenAccount account) {","sourceCodeStart":45,"sourceCodeEnd":81,"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#L45-L81","documentation":"After token validation, the confirm-registration action additionally requires that the registration-verified flag was previously stored in the webflow request context. If isAccountRegistrationVerified returns false, the user is attempting to finish registration without having completed the verification step, and a FailedLoginException is thrown.","triggerScenarios":"doExecuteInternal reaches the guard when no prior successful token verification stored the account (e.g. user jumps directly to the confirm step, replayed flow execution, or flow state was resumed out of order).","commonSituations":"User bookmarks/back-navigates into the confirmation screen; flow execution id reused or expired; session loss between verification and confirmation steps; customizing the webflow and skipping the verification state.","solutions":["Restart the MFA account registration flow from the beginning so the token verification step runs before confirmation","Do not reuse or manually resume old webflow executions; start a fresh login/registration attempt","If customizing the flow, ensure the state that calls accountRegistrationVerified(...) executes before the confirm action","Check that the webflow scope holding the verified flag is not cleared (session/flow scope configuration)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before invoking the confirm step, ensure verification happened\n// (in custom flow code) check flow scope flag before transitioning to confirm\nboolean verified = requestContext.getFlowScope().contains(\"googleAuthenticatorRegistrationVerified\");","typeGuard":null,"tryCatchPattern":"try {\n    event = action.execute(requestContext);\n} catch (FailedLoginException e) {\n    // redirect user to restart the registration flow from the first step\n}","preventionTips":["Never deep-link into later webflow states","Run states in order: verify token -> confirm registration","Avoid resuming expired webflow executions","Keep session/flow scope intact between steps"],"tags":["webflow","mfa","registration","state"],"backgroundTag":"invalid-state-transition","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"}