Multi-Factor Authentication (MFA)
Who this guide is for: Tenant administrators who want to add a second sign-in factor (an authenticator-app code) for their organization, and Embay platform operators managing the platform-wide MFA policy.
EmbayLMS supports time-based one-time passwords (TOTP) with any standard authenticator app (Google Authenticator, Microsoft Authenticator, 1Password, Authy, etc.), plus one-time backup codes for account recovery.
MFA is off by default. Nothing changes for your users until you enable it.
How the two configuration levels interact
| Level | Setting | Default | Effect |
|---|---|---|---|
| Platform (Embay staff) | Require MFA across all tenants | Off | Every enrolled user in every tenant is challenged at sign-in |
| Platform (Embay staff) | Require MFA for tenant admins | Off | Only the admin role is challenged, in every tenant |
| Platform (Embay staff) | Allow tenants to configure MFA independently | On | Tenant admins see the Security settings page and can enable MFA themselves |
| Tenant | Enable MFA for this organization | Off | Enrolled users in this tenant are challenged at sign-in |
| Tenant | Require MFA for these roles | Empty | Users in the selected roles are prompted to enroll until they do |
Resolution order at sign-in: platform setting → tenant setting → role list.
Two important guarantees:
- A user is only challenged for a code if they have completed enrollment.
- A user whose role requires MFA but who has not enrolled yet is never locked out — they see a setup reminder on their account security page.
Enable MFA for your organization (tenant admin)
- Sign in as a tenant admin.
- Go to Settings → Security in the sidebar.
- Turn on Enable MFA for this organization.
- (Optional) Under Require MFA for these roles, select the roles that must enroll (e.g. Admin, Manager).
- Click Save changes.
From this point, users who have enrolled an authenticator app are asked for a 6-digit code after entering their password. Users without one continue to sign in with just their password (unless their role is in the required list, in which case they are reminded to enroll).
Configuration reference
| Field | Values | Default | Notes |
|---|---|---|---|
| Enable MFA for this organization | On / Off | Off | Master switch for the tenant |
| Require MFA for these roles | Learner, Author, Manager, Group Admin, Admin | None | Only meaningful while MFA is enabled |
What users see (enrollment)
- Users open their avatar menu → My Profile → Two-factor
authentication → Manage (or go directly to
/account/security). - They click Set up two-factor authentication, scan the QR code with their authenticator app, and confirm with a 6-digit code.
- EmbayLMS shows 10 one-time backup codes exactly once. Users should store them safely — each code can replace an authenticator code at sign-in once (e.g. lost phone).
Recovering a locked-out user
If a user loses both their authenticator device and their backup codes, a
tenant admin can disable MFA for that user (the mfa.disable operation
requires the user:update permission). The user can then sign in with their
password and re-enroll. The action is written to the audit log.
Security notes (SOC 2)
- TOTP seeds are stored AES-256-GCM-encrypted — never in plaintext.
- Backup codes are stored as bcrypt hashes and are strictly one-time.
- All enrollment, disablement, and policy changes are written to the
append-only audit log (
resource_type: user_mfa/tenant_config). - Codes accepted with ±1 time-step (30 s) of clock drift.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| User is not asked for a code despite enrolling | MFA not enabled at tenant or platform level | Enable it under Settings → Security |
| ”Invalid authentication code” at sign-in | Device clock drift beyond ±30 s, or wrong account in the app | Sync the device clock; verify the right entry (“EmbayLMS (Your Org)”) is used |
| Security settings page is read-only | Platform has disabled tenant-level MFA configuration | Contact Embay support |
| User lost device and backup codes | — | Admin disables MFA for the user; user re-enrolls |
| Code accepted in the app but rejected during enrollment | The QR was re-generated after the app entry was added | Delete the stale entry in the app and scan the latest QR |