#Multi-Factor Authentication (MFA)
#Overview
Multi-factor authentication adds an extra layer of security to your AiDial portal account. Sign-in, authenticator setup, MFA challenge, and recovery-code entry are handled by AiDial's identity provider, while the portal checks that your account satisfies the required MFA policy before non-remediation protected pages and actions are available.
#Which Roles Require MFA?
| Role | MFA Requirement |
|---|---|
Client Administrator (client_admin) | Optional |
Partner Administrator (partner_admin) | Required |
AiDial Administrator (aidial_admin) | Required |
AiDial Operator (aidial_operator) | Required |
Client Manager (client_manager) | Optional |
Client Staff (client_staff) | Optional |
Partner User (partner_user) | Optional |
When MFA is required for a session, the portal treats it as compliant only after MFA is enrolled and the current sign-in has satisfied the required challenge. A non-compliant customer session goes to Account → Security, a partner session to Settings, and an internal AiDial administrator/operator session to Profile.
#Supported Authentication Factors
AiDial Portal currently recognises these MFA factors in portal MFA snapshots built from Zitadel OIDC/session state and, where available, a server-side enrolled-factor lookup:
totp: Time-based One-Time Password from a TOTP-compatible authenticator app.recovery_code: Provider-issued recovery code used during sign-in.email_otp: Email one-time code. This is policy-allowed forclient_adminandpartner_adminonly, unless a trusted provider policy claim explicitly allows it for another role.sms_otp: SMS one-time code. Policy-allowed forclient_adminandpartner_adminonly, gated by a per-tenant override (mfa.sms_otp.enabled, default OFF). Internal staff (aidial_admin,aidial_operator) are hard-denied. AU phone numbers only at launch. See sms-otp.md for the customer-facing privacy notice and sub-processor disclosure.webauthn: Passkey or security key. Policy-allowed forclient_admin,partner_admin,aidial_admin, andaidial_operator. The portal accepts Zitadel's pinneduserAMR value as WebAuthn challenge evidence and never treats genericmfaorotpas WebAuthn.
The portal reports policy-allowed factors separately from factors enrolled by the current user. Operators must not treat allowed_factors as proof that a user has enrolled the factor; use enrolled_factors, enrolled_factors_source, and the current challenge state for that. server_lookup is authoritative. amr_inference_fallback preserves conservative AMR-derived evidence but cannot prove absence and cannot distinguish OTP-class factors from one another.
The portal does not render its own authenticator QR code and does not validate TOTP, email OTP, SMS OTP, passkey, security-key, or recovery-code values directly. Those steps happen in Zitadel.
#Where MFA Settings Live
The MFA surface you see depends on your role.
| Role | Surface | What is available there |
|---|---|---|
client_admin, client_manager, client_staff | Account > Security (/account) | Trusted provider link on the password, two-factor, and recovery-code rows; recovery-code acknowledgements; active sessions |
partner_admin, partner_user | Settings > Profile & Security (/settings) | Read-only, operations-managed security block: policy, enrolment, and challenge state plus Refresh security status. No setup link, SMS card, passkey card, or disable action |
aidial_admin, aidial_operator | Profile (/profile) | Re-enrol through the provider, factor state table, recovery-code acknowledgements, self-audit link |
The richer Settings > Profile & Security panel (MFA setup link, SMS factor card, passkey card, declare recovery-code regeneration, disable-launch action) is code that no customer role reaches today: client_admin requests to /settings?section=profile are redirected to /account, and client_manager / client_staff get a not-found response there because their Profile section moved to the Account shell.
#How to Enrol in MFA
- Sign in to the portal: Use the normal portal sign-in page. If the current session requires MFA and is not compliant, the portal redirects you to the role-specific surface listed above.
- Open MFA setup: Go to the surface for your role (see Where MFA Settings Live) and use the trusted provider link when it is available — the per-method action on Account > Security for customer roles, or Re-enrol on the internal Profile page. The link opens the trusted provider security page for your account. Partner accounts do not get a setup link; partner MFA is operations-managed.
- Complete setup: For TOTP, passkeys/security keys, and recovery codes, follow the provider-hosted Zitadel flow. SMS one-time code has no reachable enrolment card today (see SMS One-Time Code).
- Refresh the portal status: Return to the portal and sign in again so it receives fresh OIDC MFA claims. Where shown, Refresh security status starts that flow for you.
- Acknowledge recovery-code storage if prompted: After enrolment, re-enablement, or declared recovery-code regeneration, the portal may ask you to confirm that you stored the current recovery-code set.
#Recovery Codes
Recovery codes are managed by Zitadel, not generated or displayed by AiDial Portal. Use them from the provider sign-in flow if you lose access to your authenticator app.
The portal stores only non-secret lifecycle markers for recovery-code acknowledgements and reminders. It does not store raw recovery codes.
#Important Guidelines
- Store recovery codes securely: Save them in a password manager or print them and keep them in a secure location.
- Acknowledge storage in the portal when prompted: This records that you stored the current provider-issued set; it does not copy the codes into the portal.
- Review or regenerate codes in the provider: If a trusted provider management link is available, use it to review or regenerate recovery codes, then refresh the portal status.
- Respond to recovery-code reminders: If the portal detects a recovery-code sign-in, your security surface may remind you to review or regenerate your codes.
- Never send raw recovery codes to support: Support can guide recovery steps, but users and operators must not paste recovery-code values, authenticator seeds, one-time codes, session cookies, or bearer tokens into tickets or chat.
#Recovery & Lockout
#If You Lose Your Authenticator Device
- Use a recovery code in Zitadel: During provider sign-in, use the recovery-code option if it is available for your account.
- Review your recovery options: After signing in with a recovery code, the portal may show a reminder in Settings to review or regenerate recovery codes.
- Set up a new authenticator device: Use the trusted provider management link from Settings when available, then refresh the portal status.
#If You Have Lost Both Your Authenticator and Recovery Codes
If you cannot access your authenticator app and have no remaining recovery codes:
- Contact your organisation's administrator: They may be able to assist with account recovery through the identity provider's administrative tools.
- Contact AiDial support: If your administrator is unable to help, contact
help@aidial.com.aufor further assistance.
The portal cannot bypass MFA on its own, reveal one-time codes, or reset MFA from an unauthenticated browser session. Account recovery is managed through the identity provider and authorised support processes.
#Operator Readiness Checks
The MFA flows depend on the portal's standard required runtime configuration: NEXTAUTH_SECRET, NEXTAUTH_URL, AIDIAL_API_BASE_URL, AIDIAL_API_TIMEOUT_MS, ZITADEL_ISSUER, ZITADEL_CLIENT_ID, and ZITADEL_CLIENT_SECRET. These values are required; the portal code does not define fallback defaults for them.
Before changing MFA provider policy beyond the current portal foundation, operators must complete these development-environment checks and record evidence in the release notes:
- Keep the pinned Zitadel AMR and enrolled-factor lookup evidence current for TOTP, recovery code, email OTP, SMS OTP, and WebAuthn/passkey.
- Confirm whether Zitadel exposes enrolled factors in OIDC claims or requires the server-side enrolled-factor lookup, and document when the portal is using AMR inference fallback.
- Verify
client_admin,partner_admin,aidial_admin, andaidial_operatorcan use WebAuthn only where policy allows it. - Verify
aidial_adminandaidial_operatorcannot satisfy mandatory MFA with email OTP or SMS OTP unless an explicit approved provider policy and portal implementation allow it. - Verify recovery-code sign-in still shows the recovery-code-first remediation path.
- Verify APP 5 collection notice copy is presented before the SMS factor card collects a phone number. The portal has no email-OTP collection surface today; email OTP appears only as a policy or enrolled-factor label.
- Verify audit metadata contains only safe factor identifiers and marker IDs, not OTPs, email addresses, phone numbers, tokens, cookies, or recovery codes.
Internal operators should consult the AiDial operations runbooks for the current pinned-claim table, recorded live evidence, and the safe path to enable email OTP in development without changing customer-facing Zitadel policy. This guide stays focused on portal end-user behaviour.
#Passkey / Security Key Gate
Passkeys and security keys are provider-hosted in Zitadel. The portal does not store WebAuthn credential IDs, public keys, attestation, assertions, challenges, or raw recovery codes. The passkey card on Settings > Profile & Security may show a device label and last-used timestamp when Zitadel's enrolled-factor lookup returns those safe fields; the card is hidden unless webauthn is policy-allowed for your role or already enrolled. The internal Profile factor table shows factor state and last-used only.
The portal accepts only the pinned Zitadel user AMR value as WebAuthn challenge evidence. Generic mfa or otp AMR values are not enough to identify a passkey/security-key challenge.
Recovery codes remain provider-managed. Because Zitadel maps recovery-code authentication to generic otp, the portal does not infer recovery-code use from otp; recovery-code lifecycle reminders continue to rely on the existing explicit marker paths.
#SMS OTP Gate
SMS OTP enrolment is implemented but remains deny-by-default unless both the role policy and tenant override allow it. The SMS factor card is coded only on the Settings > Profile & Security panel, and it is hidden there unless allowed_factors includes sms_otp (which folds the role and tenant-override checks together). When it renders, it shows the APP 5 notice before collecting an Australian +61 mobile number. partner_admin is policy-allowed at the enrolment route but sees the read-only operations-managed block instead, and client_admin is redirected off that panel to the Account shell, so no role has a self-service enrolment path through supported navigation.
POST /api/auth/mfa-sms-enrol enforces CSRF/session auth, permits only client_admin and partner_admin, requires the mfa.sms_otp.enabled tenant override, validates AU E.164 numbers, applies rate limits, forwards the number once to Zitadel's otp_sms factor endpoint, and discards the plaintext. Responses and audit metadata use the masked phone tail only, with forbidden fields redacted.
SMS OTP challenge evidence must not be inferred from generic mfa or otp AMR values. Current parsers keep sms_otp AMR aliases empty; enrolled SMS state comes from the server-side Zitadel enrolled-factor lookup when available. If the provider integration is unavailable, SMS enrolment fails closed with a provider-unavailable response.
#Disabling MFA
- Roles that require MFA (
partner_admin,aidial_admin,aidial_operator) cannot disable MFA while assigned to that role. - The disable-launch action is coded on the Settings > Profile & Security panel only, which customer roles are currently redirected away from, so no role reaches it through supported navigation. Where it does render, it appears only when MFA is not required for the role, the account is currently enrolled, the challenge state is known, and a trusted provider management URL is available.
- Account > Security and the internal Profile page do not offer a disable action. They link out to the trusted provider security page and record recovery-code acknowledgements. Remove the factor in the provider instead.
- The portal does not disable the factor directly. It records the launch request and opens the provider management flow in a new tab.
- After changing MFA in the provider, refresh the portal security status so the current session reflects the new state.