apereo/cas · error

Google authenticator token credential is not assigned a…

Error message

Google authenticator token credential is not assigned a valid account id

What it means

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.

Solutions

  1. Ensure the client-side registration form submits the correct hidden accountId field for the selected device
  2. Reconstruct the credential from current request parameters so accountId is populated from the same registration the user selected
  3. Verify the account lookup resolves the registration matching the credential's accountId
  4. Clear stale browser session/form state and retry the registration-validation flow

Example fix

// before
class GoogleAuthenticatorTokenCredential { private Long accountId; /* never set */ }
// after
class GoogleAuthenticatorTokenCredential {
    GoogleAuthenticatorTokenCredential(String token, Long accountId) {
        this.token = token;
        this.accountId = Objects.requireNonNull(accountId, "accountId required");
    }
}
Defensive patterns

Strategy: validation

Validate before calling

if (credential.getAccountId() == null || !credential.getAccountId().equals(account.getId())) {
    throw new IllegalStateException("Credential accountId missing/mismatch for registration " + account.getId());
}

Type guard

boolean hasValidAccountId(var c) { return c.getAccountId() != null && c.getAccountId() > 0; }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/3cd8bd095781e3c9. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/web/flow/GoogleAuthenticatorValidateSelectedRegistrationAction.java:53

            LOGGER.info("Multifactor authentication device [{}] is trusted with fingerprint [{}]", trustedDevice.getName(), trustedDevice.getDeviceFingerprint());
            return success(trustedDevice);
        }

        val account = MultifactorAuthenticationWebflowUtils.getOneTimeTokenAccount(requestContext, OneTimeTokenAccount.class);
        if (account == null) {
            LOGGER.warn("Unable to determine google authenticator account");
            addErrorMessageToContext(requestContext);
            return error();
        }
        val credential = WebUtils.getCredential(requestContext, GoogleAuthenticatorTokenCredential.class);
        if (credential == null) {
            LOGGER.warn("Unable to determine google authenticator token credential");
            addErrorMessageToContext(requestContext);
            return error();
        }
        LOGGER.trace("Located account [{}] to be used for credential [{}]", account, credential);
        if (credential.getAccountId() == null || credential.getAccountId() != account.getId()) {
            LOGGER.warn("Google authenticator token credential is not assigned a valid account id");
            addErrorMessageToContext(requestContext);
            return error();
        }
        return null;
    }
}

View on GitHub (pinned to e7288fc434)