2FA and account management (updated 2026-10-18 with Authelia + Medusa core MFA)
2FA and account management (updated 2026-10-18 with Authelia + Medusa core MFA)
Update: Authelia as L1 gate (Tam finding — authelia-optimization.md)
Tam found Authelia (github.com/authelia/authelia, 29,294 stars, Apache-2.0) — an SSO/MFA portal for web apps with reverse-proxy forward-auth. This changes the 2FA architecture: we don't need to build 2FA into Medusa. Instead, Authelia sits in front of everything as the platform gate.
Three security layers (no duplication)
| Layer | Scope | Tool | What it covers |
|---|---|---|---|
| L1 — Platform gate | All vhosts (docs, pulse, tenant KBs, staging) | Authelia (self-hosted, Apache-2.0) | SSO + TOTP 2FA + audit, reverse-proxy forward-auth |
| L2 — Commerce identity | Medusa admin/staff accounts | Medusa v2 core MFA (TOTP + recovery codes, env knob AUTH_MFA_ENCRYPTION_KEY) | Commerce-specific auth |
| L3 — Agent identity | EVE chat users | Better Auth 2FA plugin (already in EVE templates) | Agent/chat user auth |
What Medusa covers natively (don't double-build)
- Medusa v2 core has built-in MFA (TOTP + recovery codes) — one env knob
- EVE templates already carry Better Auth 2FA plugin
- So: Medusa handles commerce auth, Better Auth handles EVE auth, Authelia handles everything else (vhost gate, SSO across sites)
Authelia deployment on prod
- Compose project cib-authelia on prod, loopback bind :9091
- nginx forward-auth (authelia.conf include)
- TOTP + recovery codes from day 1
- User DB: YAML file at fleet size (5 users), migrate to backend when tenants >3
- ~50 MB RAM, $0 licence
- Retires basic-auth (htpasswd migrated to Authelia users)
Build order
- Compose file + config (Ace) → deploy on prod
- nginx include for docs.youngle.au forward-auth → verify TOTP login
- Migrate htpasswd users → set TOTP → cut-over → retire basic-auth
- Template tenant policy block (sellable "private KB with 2FA" feature)
Medusa admin account management optimization
Medusa V2 Admin (/app) already provides:
- User management (invite, edit, delete) — ✅ verified
- Password reset via email / copy-link (v2.20.0+) — ✅ verified
- MFA (TOTP + recovery codes via env knob) — ✅ verified, in core since v2.15.5
🔴 Correction (Echo, 2026-10-18) — two claims in the list above are wrong. See
## Verification (Echo)at the foot of this document:
- "Built-in roles (admin, staff, read-only)" — ❌ RBAC is a paid Medusa Enterprise feature, not MIT core. Not available to us without a commercial licence.
- "Activity tracking (audit log)" — ⚠️ unverified; I could not find an audit-log feature in the MIT documentation.
Also: Medusa Admin cannot be restyled or re-laid-out — only widgets on existing pages and new UI routes are supported.
What we add:
- 2FA enrollment page in Medusa Admin (custom route) — note: widgets/UI routes are the only hooks; heavier UX belongs in the Nuxt operator layer
- TOTP secrets encrypted at rest (Medusa metadata field, encrypted)
- Recovery codes: generate 10, store hashed, one-time use
- Rate limit 2FA attempts (5 per 15 min)
- Admin accounts MUST have 2FA enforced at provisioning
What we don't build:
- WebAuthn/passkeys (Phase 2)
- Cross-tenant operator dashboard (Phase 2 — Nuxt UI Pro)
- SSO/SAML (enterprise, later)
Account management features needed
Per-tenant
- User registration (Medusa admin API)
- Role-based access (admin/staff/read-only — Medusa built-in)
- Password reset (email-based — Medusa built-in)
- 2FA enrollment (TOTP setup with QR code + recovery codes)
- 2FA verification (required on every login)
- Session management (list active sessions, revoke)
- Audit log (Medusa built-in activity tracking)
Cross-tenant (our operator view)
- Tenant provisioning (create tenant, generate credentials, send welcome email)
- Tenant suspension (disable for non-payment)
- Tenant deletion (remove data with backup)
- Usage monitoring (per-tenant resource usage)
- Billing integration (connect to payment provider)
Security considerations
- TOTP secrets encrypted at rest
- Recovery codes: generate 10, store hashed, one-time use
- Rate limit 2FA attempts (5 per 15 min)
- Log all 2FA events (enrollment, verification, failure)
- Admin accounts MUST have 2FA — enforce at provisioning
- Authelia TOTP secrets stored in Authelia own backend (YAML/SQLite)
What is in scope for MVP
- TOTP 2FA for admin accounts (enforced via Medusa core MFA)
- Email OTP for recovery
Basic role management (admin/staff/read-only)— 🔴 REMOVED: RBAC is a paid Enterprise feature (Echo, 2026-10-18). Pilot = all admin users are full admins. See## Verification (Echo)§2.- Audit log (Medusa built-in) — ⚠️ unverified, see
## Verification (Echo)§5 - Authelia L1 gate for all vhosts
What is NOT in scope for MVP
- WebAuthn/passkeys
- Cross-tenant operator dashboard (Phase 2)
- Billing integration (Mira lane)
- SSO/SAML (enterprise, later)
- Authelia LDAP/Active Directory integration (later)
Verification (Echo) — Medusa Admin capability, read from the docs
Young asked: "Can we optimise Medusa Admin for account management?" Yes — and the optimisation is to build less. Most of what this doc proposed building already ships in Medusa. Three corrections, each sourced.
1. MFA is core — one env var, no plugin
MFA ships as a first-class Auth Module feature since v2.15.5.1 The Auth
Module's feature list names "time-based one-time passwords (TOTP), backup
recovery codes, and configurable disable policies."2 Two providers are
registered out of the box: totp and recovery_code.1 Configuration is
AUTH_MFA_ENCRYPTION_KEY — auto-generated for apps created after v2.15.5; if
unset you get a clear error at enrol time, not silence.1
Two policy knobs this doc did not know about, both worth adopting:1
disable_policy: challenge— requires re-verification (TOTP/recovery code) before MFA can be removed. The docs recommend it "to protect users from a stolen session being used to remove their MFA factor." Stronger than "enforce at provisioning".requireMfa: { maxAgeSeconds: N }— demands a recent MFA challenge for elevated actions (password change, large payment). This is the right control for refunds and pricing changes in the 3-in-1 package.
2. 🔴 RBAC is a paid Enterprise feature — not core
This doc lists "Built-in roles (admin, staff, read-only)". Medusa's
ENTERPRISE-LICENSE.md says the opposite:8
"RBAC (role-based access control) and SSO (single sign-on) are Medusa Enterprise Edition features."
The proprietary paths include the RBAC module, its admin APIs, and the roles and policies Admin dashboard routes.8 The licence adds that "Enabling any Enterprise feature flag constitutes a representation that you hold a valid commercial agreement", and that circumventing feature-gating is itself a violation.8
| Account feature | Licence | Ship it? |
|---|---|---|
| Users, invites, password reset | MIT core | ✅ |
| TOTP MFA + recovery codes | MIT core | ✅ |
| Roles / permissions (RBAC) | 🔴 Enterprise | ❌ no deal |
| SSO / OIDC | 🔴 Enterprise | ❌ no deal |
Options, stated plainly: (1) drop role separation for the pilot — all admins are full admins; (2) gate roles in our Nuxt operator layer — but that only covers our dashboard, not Medusa Admin's own API, so it is not an enforced boundary; (3) buy the Enterprise licence when a tenant genuinely needs enforced roles. Recommendation: (1) for the pilot, with (3) as the named trigger — the moment a tenant has staff who should not see margin or pricing, RBAC becomes a licence line, not build labour. Mira: that is a pricing fact.
3. Admin account management already ships — use it
Verified natively in the Admin user guide:3
| Capability | Native | Detail |
|---|---|---|
| View / search / filter users | ✅ | Settings → Users |
| Edit name + metadata | ✅ | key-value metadata3 |
| Invite (not create) | ✅ | "You can't create a user. Instead, you need to invite them."3 |
| Invite expiry + token refresh | ✅ | User Module6 |
| Copy password-reset link | ✅ | since v2.20.03 |
| Delete user | ✅ | irreversible, since v2.12.03 |
The invite model is better than what this doc proposed — no password ever passes through us. Provisioning should create an invite, not a user.
4. The real Admin constraint — why the hybrid ruling is right
The Admin dashboard is deliberately closed:7
"You can't customize the admin dashboard's layout, design, or the content of the existing pages (aside from injecting widgets). You also can't change the Medusa logo."7
Only Widgets and UI Routes are supported; for anything heavier the docs point to building a custom dashboard against the Admin API.7 That confirms Young's hybrid ruling exactly: Medusa Admin = commerce ops + native user/invite management; Nuxt UI Pro = our operator layer on the Admin API (tenant provisioning, usage, cross-tenant view, any account UX we want to own).
5. ⚠️ Audit log — unverified, not confirmed
This doc asserts "Audit log (Medusa built-in activity tracking)". I could not find an audit-log feature in the MIT documentation I read. It may exist under another name — treat it as unverified until someone finds the page. (Tam's Authelia doc claims audit for L1, a different surface.)
6. Integration decision the two docs do not settle
Authelia (L1) and Medusa MFA (L2) are disjoint by design, but the boundary needs one ruling:
- Authelia protects surfaces with no application login (static Docus).
- Medusa MFA protects Medusa identities.
- 🔴 A Medusa Admin placed behind Authelia would prompt twice. Do not forward-auth a tenant's Medusa Admin vhost through Authelia — the tenant admin should get Medusa's MFA, and Authelia stays off that vhost. Sol's to rule.
7. What this means for the 3-in-1 package
- The trust layer gains a second guard.
requireMfa: {maxAgeSeconds}lets a HIL-approved write demand a recent challenge — a control unavailable when the package architecture was written.1 - No role separation in the pilot — the package cannot promise "staff with limited access" without an Enterprise licence.8
- Security posture is a selling line at A$0 software — Authelia L1 + Medusa core MFA L2 + Better Auth L3. The only paid item is Enterprise RBAC, and only if a tenant needs it.
Honest limits
- I read the documentation and the licence file, not a running Medusa Admin. I did not exercise an MFA enrolment end-to-end.
- Our pinned v2.21.2 satisfies MFA's v2.15.5+1 and reset-link v2.20.03 requirements — verified from our docs, not from a running container.
- The audit-log claim is unverified (§5) — I looked and did not find it.
- I did not price the Medusa Enterprise licence; it is "contact sales", so the RBAC cost is unknown, not zero.
- The double-prompt interaction in §6 is my reading of the two layers; it has not been tested on a live vhost.
Updated by
- Sol (2026-10-18): initial spec
- Tam (2026-10-18): Authelia deployment design (authelia-optimization.md)
- Mira (2026-10-18): cost framing (Authelia $0 licence, ~50MB RAM, ~1 day build)
- Nova (2026-10-18): UX for 2FA enrollment flow, recovery codes, device management
- Echo (2026-10-18): verified Medusa Admin capability against the docs — MFA is core (one env var), RBAC is paid Enterprise (out of MVP), audit log unverified, Admin is widget-only (confirms the hybrid ruling)
Sources
1 https://docs.medusajs.com/resources/commerce-modules/auth/mfa — Medusa — MFA (Auth Module) 2 https://docs.medusajs.com/resources/commerce-modules/auth — Medusa — Auth Module overview 3 https://docs.medusajs.com/user-guide/settings/users — Medusa — Manage Users in Admin (user guide) 6 https://docs.medusajs.com/resources/commerce-modules/user/user-creation-flows — Medusa — User Creation Flows (invites, expiry) 7 https://docs.medusajs.com/learn/fundamentals/admin — Medusa — Admin Development (widgets, UI routes, limits) 8 https://raw.githubusercontent.com/medusajs/medusa/develop/ENTERPRISE-LICENSE.md — Medusa — Enterprise Edition License (RBAC + SSO proprietary)

