Security layer — 2FA and account management
Security layer — 2FA and account management
Young, 2026-10-18: "Another features that we need to add as security layer, pls share your thought everyone: 2FA and account management"
My read in one line: we do not need to build an identity system. Both halves already exist in what we are already deploying — Medusa ships MFA in core for commerce actors, and Authelia (Apache-2.0) covers the gated-static sites (docs, Pulse) that Medusa never sees. The work is wiring two existing pieces, not inventing a third.
1. The problem, stated precisely
We have three different kinds of login on the tenancy platform, and they are not the same problem:
| Surface | Who logs in | Example |
|---|---|---|
| Commerce storefront | the tenant's customers | a shop's shopper |
| Commerce admin | the tenant's staff | shop owner managing orders |
| Gated static sites | our people + the tenant | docs.youngle.au (private), Pulse (public) |
A single answer does not fit all three. Attempting one would either over-build the storefront or under-protect the docs site.
2. What already exists (measured today)
2a. Medusa MFA — built into the core Auth Module
Medusa ships TOTP multi-factor authentication as part of the core Auth Module, since v2.15.5.[4]5 It requires no custom auth provider and no hosted auth service.5
The flow, as documented:
- The auth provider verifies the first factor (email/password, GitHub OAuth, or a custom provider).5
- Medusa checks whether the authenticated
auth_identityhas MFA enabled.5 - If enabled, no token is issued. Instead Medusa returns a short-lived
challenge:
{ mfa_required: true, mfa_challenge: { id, methods: ["totp" | "recovery_code"], expires_at } }.5 - The user completes it with an authenticator app code (1Password, Authy, Google Authenticator, Microsoft Authenticator) or a recovery code, and only then is the token minted.5
Setup returns { mfa: {id, provider: "totp", status: "pending"}, secret, otpauth_url }
— the otpauth_url renders as a QR code.5 Recovery codes are included,
which is the part teams forget and then get locked out over.5
It works across actor types — the same flow protects admin users, customers,
or custom actors.5 Configuration is one environment variable,
AUTH_MFA_ENCRYPTION_KEY (a 64-character random string; auto-generated for apps
created after v2.15.5).4
2b. Authelia — for everything Medusa does not own
docs.youngle.au is currently behind nginx basic-auth (I measured a 401
to anonymous requests). Basic-auth is a single shared password with no second
factor, no user accounts, and no audit trail. For a private docs site holding
our internal vault content, that is the weakest link in the whole platform.
Authelia is an open-source authentication and authorization server providing two-factor authentication and single sign-on (SSO) for applications via a web portal, acting as a companion for reverse proxies — which is exactly our nginx front door.[7]17 It is Apache-2.0 licensed.14 It integrates with a reverse proxy through a forward-auth / proxy-authorization endpoint, and the proxy must pass a defined set of headers.7
Why this is the right fit: it protects sites that have no application login of their own — a static Docus build cannot authenticate anyone. Authelia sits in front at the proxy layer, so the site needs no code change.
2c. Better Auth — already inside the eve Web Chat Agent
The eve Web Chat Agent template ships with Better Auth for per-user memory.1 Better Auth's 2FA plugin supports TOTP, OTP (email/SMS), backup codes, and trusted devices — and by default requires a password to enable 2FA.6 So the EVE receptionist product inherits an account layer without us writing one.
3. Proposed security layer — three tiers, two components
| Layer | Component | Covers | Cost |
|---|---|---|---|
| L1 — Platform gate | Authelia (Apache-2.0) | docs site, Pulse, any static/private vhost | A$0 software |
| L2 — Commerce identity | Medusa Auth + MFA (core) | storefront customers, tenant admin staff | A$0 (in core) |
| L3 — Agent identity | Better Auth (in eve templates) | EVE chat users, per-user memory | A$0 (in template) |
All three are open-source and already in our stack. The security layer adds no licence cost — it is wiring plus configuration.
3a. Why Authelia rather than something else
- It solves the problem nginx basic-auth cannot: real accounts, TOTP second factor, SSO across multiple gated vhosts, and an audit trail.
- It is proxy-level, so it protects static sites with zero application changes — critical because Docus is a static build.
- Apache-2.0 — resellable and self-hostable, consistent with our licence-clean rule.
3b. Account management — what "management" should mean here
"Account management" is three distinct jobs; conflating them is how systems get built twice:
- Self-service — the user enrols their own TOTP, generates recovery codes, and revokes a lost device. Medusa MFA and Better Auth both provide this; it should be the user's job, not a support ticket.
- Tenant administration — the tenant's own admin invites/removes their staff and resets their access. Medusa's admin layer carries this.
- Platform administration — we provision a tenant, issue their first admin, and can revoke at the platform level. This is our job and should be a documented runbook, not an ad-hoc shell command.
The rule I would set: the tenant owns their users; we own the tenant's existence. We should never be in the business of holding a tenant's staff passwords.
4. Where 2FA should be mandatory vs offered
Not every login deserves the same friction. My recommendation:
| Surface | 2FA posture | Reason |
|---|---|---|
| Tenant admin / staff | Mandatory | they can see orders, customers, money |
| Platform admin (us) | Mandatory | we hold every tenant's keys |
| Private docs site | Mandatory (Authelia) | holds our internal vault content |
| Storefront customers | Offered, not forced | forced 2FA on shoppers measurably costs sales |
| Public sites (Pulse) | None | public by design |
This is the defensible middle: protect what holds money and private data, do not put friction in front of a shopper's checkout.
5. What I would flag before we build
- Basic-auth on the docs site is the current weakest link. It is one shared password in front of our internal vault mirror, with no second factor and no audit trail. Authelia is the fix, and it is the highest-value item here.
AUTH_MFA_ENCRYPTION_KEYis a new secret to manage. It encrypts MFA factors — losing it locks every enrolled user out. It belongs in the tenant's env file at mode600, never in a unit file (see the credential finding in my vault verification — prod currently inlines API keys in a world-readable unit).- Recovery codes are not optional. Both Medusa and Better Auth ship them; if we do not surface them during enrolment, our support load becomes password resets forever.
- Do not build a fourth auth system. We already have three; the discipline is to use each for the surface it owns.
Honest limits
- I verified capability from the vendors' own docs, not by deploying and
exercising each flow. Medusa MFA requires v2.15.5+ — confirm our pinned
version satisfies it before promising the feature. Update 2026-10-21: the
Authelia half of this is no longer a proposal — it is live on prod aarch64
(deployed by sol). I re-probed from LAB:
auth.youngle.aureturns 200 on a Let's Encrypt SAN cert covering all 11youngle.ausubdomains (tls_verify=0), anddocs.youngle.au+solos.youngle.aureturn 302 → auth.youngle.au, so the L1 platform gate is real. What remains unproven is the Medusa MFA flow itself (deployed, but I have not enrolled a TOTP actor). - No penetration testing has been done. This study proposes an architecture; it is not a security audit of the current platform.
- The
401ondocs.youngle.auconfirms something gates it; I read the nginx config to confirm basic-auth, and did not attempt to authenticate.
Sources
Sources
1 https://eve.dev/templates — eve — Templates (agent catalogue) 4 https://docs.medusajs.com/resources/commerce-modules/auth/mfa — Medusa — Multi-Factor Authentication (Auth Module) 5 https://medusajs.com/blog/announcing-mfa — Medusa — Announcing multi-factor authentication (2026-06-04) 6 https://better-auth.com/docs/plugins/2fa — Better Auth — Two-Factor Authentication plugin 7 https://www.authelia.com/integration/proxies/introduction — Authelia — Proxies integration 8 https://hub.docker.com/r/authelia/authelia — Authelia — Docker image 14 https://github.com/authelia/authelia — Authelia — Apache-2.0 License (repo) 17 https://www.authelia.com — Authelia — authentication & authorization server
Products from Vercel templates — our offer catalogue (2026-10-17, Tam for Mira)
Young: "work on which products we should offer based on Vercel Example — they give us a platform to showcase our services. All repos live-verified via GitHub API today.
Stage 0 verified — the gate is real, but the 2FA half is not switched on
Verified on 161.33.71.67, 2026-10-19. Stage 0's headline claim is true and Stage 1's artifacts are real. But Stage 0's proof line "TOTP enrollment + verification succeeds" is not — TOTP is configured but not enforced, and no user has enrolled. Two further hygiene defects below.

