Two-factor authentication during sign-in
M7 consumer accounts can link an authenticator and choose where a code is required in two-factor settings. Enrollment supports a QR code or manual setup key for a standard TOTP app using SHA-1, six digits, and a 30-second period.
Applications continue to use authorization code with PKCE or Device authorization. M7 hosts the password, authenticator, and recovery screens. Your application must not collect an M7 authenticator secret, TOTP code, or recovery code, or submit those values to the OAuth token endpoint.
Where checks apply
The user controls four independent settings after linking and verifying an authenticator:
| Setting | When M7 asks for a code |
|---|---|
| Password logins | After a correct M7 password during regular browser authorization, before completing that password sign-in. |
| Email sign-in | After successful magic-link or email-code proof during consumer browser authorization. |
| Device Code | After a correct M7 password during Device Code approval, before completing approval. |
| Account switches and existing-session sign-ins | When selecting a remembered consumer account or using an account already open in the browser to authorize an application, subject to the selected interval. |
These settings apply to consumer accounts. The password and Device Code settings do not depend on each other. Email sign-in has its own setting, which defaults to off and applies to both email methods. See email sign-in for browser binding, confirmation, and expiry.
A fresh federated sign-in uses the provider's authentication policy and does not add the local-password TOTP step. Later reuse of that remembered account follows M7's account/session setting, even when the account originally signed in through federation.
Refresh-token requests and machine client_credentials issuance do not prompt
for these codes. Refreshing a token does not restart the account-verification
freshness interval. Linking an authenticator, or enabling one setting, does
not mean every login path is subject to the same check.
Account switches and existing sessions
The interval choices are Every time, 15 minutes, one hour, and eight hours. A missing or expired verification requires a new code. Within a nonzero interval, recent successful verification for the same account in the same browser session can satisfy the check. Another account, another browser, or a replacement authenticator does not inherit that proof.
Every time means one proof for each new protected authorization transaction, not another prompt on every page within the same transaction. Selecting an account and continuing its authorization should not require duplicate codes. Using an existing M7 login to enter a different application is also an authorization transaction; it is not a refresh-token request.
The verification interval is an M7 account setting. It is separate from the
OIDC max_age parameter and does not create a new token lifetime or refresh
policy.
What an application receives
On success, the normal authorization callback and code exchange remain
unchanged. Validate state, use the transaction's PKCE verifier, and follow
the existing callback contract.
Do not infer an amr or acr guarantee from the fact that an M7 account has
enabled one of these settings.
For prompt=none, an existing session can still require a code. When that
account-verification step is due and callback trust has been established,
M7 returns error=login_required with
error_reason=account_two_factor_required to the registered callback. Validate
state and start a new interactive authorization without prompt=none when
the user chooses to continue. Do not repeatedly retry the silent request.
During Device Code approval, token pickup remains pending until the required
sign-in, two-factor proof, and approval complete. Continue respecting the
returned polling interval and the existing authorization_pending,
slow_down, denial, and expiry behavior. The application never receives the
user's TOTP or recovery code.
An invalid or expired code does not complete sign-in. M7 applies proof-attempt limits and may require restarting the flow. A successful TOTP time step is not reusable; wait for the app's next code before another successful proof.
Recovery codes
After linking an authenticator, the user can generate ten recovery codes in M7 settings by confirming with a current authenticator code. The codes are shown once. Generating another set invalidates the entire previous set.
On a hosted two-factor prompt, Use a recovery code accepts one unused code in place of the authenticator code. It remains part of the ongoing sign-in: it is not a standalone password replacement or an OAuth credential.
M7's recovery screen checks Disable two-factor authentication by default:
- With that option selected, successful recovery fully unlinks the authenticator, turns all four checks off, invalidates its remaining recovery codes and pending challenges, and lets the current sign-in continue.
- With the option cleared and more than one code remaining, M7 asks for confirmation, consumes one code on success, and keeps the authenticator and settings. The user needs another unused recovery code or the authenticator to unlink later.
- Using the final recovery code always performs the full unlink, regardless of the checkbox. The server uses the current remaining count, so an older screen cannot override this rule.
Invalid or already used recovery codes do not establish proof. After recovery has unlinked the authenticator, visit settings to enroll the replacement, choose the desired checks, and generate a fresh recovery set.
Unlinking when the authenticator is unavailable
The settings page offers unlink proof using a current authenticator code, an unused recovery code, or an eligible existing linked GitHub or Discord identity. For linked-account proof, M7 takes the user through the selected provider and returns to settings for an explicit final confirmation. The identity must match the already linked account; cancellation does not unlink the authenticator.
Successful unlink turns all checks off and invalidates the authenticator's recovery codes and pending challenges. It leaves the identity provider linked to the M7 account. Provider proof may reuse the provider's session; it does not guarantee a fresh provider password prompt or provider-side MFA.
For account-management integrations, see the API User two-factor contract. Treat setup keys and recovery codes as secrets. Keep them out of application logs and URLs, and store recovery codes separately from the authenticator.