{"record":{"id":"3f6ab4e34d68a13d","repo":"apereo/cas","slug":"unable-to-determine-google-authenticator-token-cre","errorCode":null,"errorMessage":"Unable to determine google authenticator token credential","messagePattern":"Unable to determine google authenticator token credential","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":47,"sourceCode":"    }\n\n    @Override\n    protected @Nullable Event doExecuteInternal(final RequestContext requestContext) {\n        if (MultifactorAuthenticationTrustUtils.isMultifactorAuthenticationTrustedInScope(requestContext)) {\n            val trustedDevice = MultifactorAuthenticationTrustUtils.getMultifactorAuthenticationTrustRecord(requestContext, MultifactorAuthenticationTrustRecord.class).orElseThrow();\n            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":29,"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#L29-L60","documentation":"The same action next fetches the GoogleAuthenticatorTokenCredential from the request context. If no credential was submitted (token form not filled or bound) the action logs this warning, adds an error message, and returns error(). A related guard also rejects credentials whose accountId does not match the selected account.","triggerScenarios":"doExecuteInternal runs with WebUtils.getCredential(requestContext, GoogleAuthenticatorTokenCredential.class) returning null — the credential was never created because the token field was empty or the prior state did not bind it; also when credential.getAccountId() is null or != account.getId().","commonSituations":"User submits the MFA form without entering a code; form binding misconfigured so GoogleAuthenticatorTokenCredential is not instantiated; user switched selected device but the submitted credential still references the old accountId; direct POST to the endpoint bypassing the flow.","solutions":["Ensure the token input field is submitted (non-empty) so the credential is created and bound in the prior flow state","Re-select the correct device so the credential's accountId matches the account loaded into scope","Check the MFA webflow form binding/bean configuration so GoogleAuthenticatorTokenCredential is instantiated from the request","Perform the login through the normal webflow (not direct requests) so scope and credential are populated"],"exampleFix":"// before: skipping the token field\n// POST ...token= (empty)\n// after: always submit a fresh 6-digit code bound to the selected device\n// POST ... token=123456&accountId=42","handlingStrategy":"validation","validationCode":"GoogleAuthenticatorTokenCredential cred =\n    WebUtils.getCredential(requestContext, GoogleAuthenticatorTokenCredential.class);\nboolean ready = cred != null\n    && cred.getToken() != null && cred.getToken().matches(\"\\\\d{6}\")\n    && cred.getAccountId() != null;","typeGuard":null,"tryCatchPattern":"Event e = action.execute(requestContext);\nif (\"error\".equals(e.getId())) {\n    // re-render the token form so a credential is bound on resubmit\n}","preventionTips":["Require the token field (server-side and client-side) before submission","Check form binding configuration for GoogleAuthenticatorTokenCredential","Ensure the submitted accountId matches the selected device","Block direct requests that bypass webflow state population"],"tags":["webflow","mfa","credential","binding"],"backgroundTag":"missing-required-argument","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"}