{"record":{"id":"3cd8bd095781e3c9","repo":"apereo/cas","slug":"google-authenticator-token-credential-is-not-assig","errorCode":null,"errorMessage":"Google authenticator token credential is not assigned a valid account id","messagePattern":"Google authenticator token credential is not assigned a valid account id","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/web/flow/GoogleAuthenticatorValidateSelectedRegistrationAction.java","lineNumber":53,"sourceCode":"            LOGGER.info(\"Multifactor authentication device [{}] is trusted with fingerprint [{}]\", trustedDevice.getName(), trustedDevice.getDeviceFingerprint());\n            return success(trustedDevice);\n        }\n\n        val account = MultifactorAuthenticationWebflowUtils.getOneTimeTokenAccount(requestContext, OneTimeTokenAccount.class);\n        if (account == null) {\n            LOGGER.warn(\"Unable to determine google authenticator account\");\n            addErrorMessageToContext(requestContext);\n            return error();\n        }\n        val credential = WebUtils.getCredential(requestContext, GoogleAuthenticatorTokenCredential.class);\n        if (credential == null) {\n            LOGGER.warn(\"Unable to determine google authenticator token credential\");\n            addErrorMessageToContext(requestContext);\n            return error();\n        }\n        LOGGER.trace(\"Located account [{}] to be used for credential [{}]\", account, credential);\n        if (credential.getAccountId() == null || credential.getAccountId() != account.getId()) {\n            LOGGER.warn(\"Google authenticator token credential is not assigned a valid account id\");\n            addErrorMessageToContext(requestContext);\n            return error();\n        }\n        return null;\n    }\n}\n","sourceCodeStart":35,"sourceCodeEnd":60,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/web/flow/GoogleAuthenticatorValidateSelectedRegistrationAction.java#L35-L60","documentation":"During the Google Authenticator validate-selected-registration webflow step, CAS loads the registration account matching the request but the submitted token credential carries a null accountId or one that does not equal the located account's id. This is a cross-check that the credential being validated actually belongs to the registration the user selected. On mismatch the action logs a warning, adds an error message to the flow context, and returns an error outcome.","triggerScenarios":"Credential built by the client/registration flow with accountId left unset, or the credential's accountId refers to a different registration than the one resolved for the current principal (e.g. stale form data, multiple devices registered, tampered request).","commonSituations":"User has multiple registered GAuth devices and the browser posted a stale form; custom client code constructs GoogleAuthenticatorTokenCredential without calling setAccountId; session/form data from a previous registration is replayed.","solutions":["Ensure the client-side registration form submits the correct hidden accountId field for the selected device","Reconstruct the credential from current request parameters so accountId is populated from the same registration the user selected","Verify the account lookup resolves the registration matching the credential's accountId","Clear stale browser session/form state and retry the registration-validation flow"],"exampleFix":"// before\nclass GoogleAuthenticatorTokenCredential { private Long accountId; /* never set */ }\n// after\nclass GoogleAuthenticatorTokenCredential {\n    GoogleAuthenticatorTokenCredential(String token, Long accountId) {\n        this.token = token;\n        this.accountId = Objects.requireNonNull(accountId, \"accountId required\");\n    }\n}","handlingStrategy":"validation","validationCode":"if (credential.getAccountId() == null || !credential.getAccountId().equals(account.getId())) {\n    throw new IllegalStateException(\"Credential accountId missing/mismatch for registration \" + account.getId());\n}","typeGuard":"boolean hasValidAccountId(var c) { return c.getAccountId() != null && c.getAccountId() > 0; }","tryCatchPattern":null,"preventionTips":["Always set accountId on the token credential at construction time from the same form field as the token","Keep the selected-registration id in the webflow scope and bind it to the credential server-side","Include the accountId as a required hidden field in the registration form"],"tags":["gauth","mfa","webflow","account-id-mismatch"],"backgroundTag":"invalid-identifier","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"}