Admin¶
Active production application
Admin is deployed by both the automatic production pipeline and the manual Cloudflare recovery workflow. It is intentionally isolated from the shared browser-app packages.
Purpose¶
Internal admin app - the only app whose entire path space runs through its own Worker
(run_worker_first: true), not just specific routes.
Source directory¶
apps/admin-web/ — src/ (React frontend plus RBAC/MFA progressive-enhancement modules), worker/ (authentication, MFA, RBAC and Admin business APIs).
Framework¶
React 18 + TypeScript + Vite. Does not depend on @akshaya/ui or @akshaya/shared - the one
app in this monorepo that's deliberately isolated from the shared packages.
Commands¶
Output¶
apps/admin-web/dist/.
Deployment mechanism¶
Cloudflare Worker, name akshaya-admin, run_worker_first: true for every path.
wrangler deploy --config apps/admin-web/wrangler.jsonc. Domain: admin.akshayagroupglobal.com.
worker/entry.ts is the outer Admin security boundary. It wraps the existing password/auth Worker
with optional TOTP MFA and RBAC before delegating allowed requests to worker/index.ts.
Environment variables / secrets¶
No VITE_* build-time variables.
First-Admin bootstrap secret:
MFA encryption secret:
ADMIN_MFA_ENCRYPTION_KEY must be a strong value of at least 32 characters and is available only to
the Admin Worker. It encrypts TOTP authenticator secrets with AES-GCM before they are written to D1.
It is never sent to browser code or committed to the repository.
RBAC itself requires no separate secret.
Related domain¶
admin.akshayagroupglobal.com
Authentication¶
Admin identity requires:
- an Active shared
usersrow withrole='Admin' - PBKDF2 password verification
- failed-login lockout
- a dedicated host-only HttpOnly Admin session cookie
- four-hour Admin session lifetime
- TOTP/recovery-code verification after password authentication when that Admin has enabled MFA
The one-time first-Admin bootstrap route is /api/auth/bootstrap, gated by ADMIN_BOOTSTRAP_TOKEN and only available while no Admin exists.
Multi-factor authentication¶
Migration 0014_admin_mfa.sql adds:
admin_mfa— encrypted TOTP secret, enabled state, confirmation time and replay-prevention stepadmin_mfa_recovery_codes— one-time recovery codes stored only as SHA-256 hashesadmin_mfa_challenges— five-minute opaque login challenges after a successful password factor
MFA is opt-in per Admin in this phase, so deployment cannot lock out existing administrators. Once an Admin enables it, password-only login no longer creates a usable Admin session. The core password login first performs the existing password/status/lockout checks; its temporary session is immediately discarded, and the outer Worker issues a five-minute HttpOnly MFA challenge instead. Only a valid TOTP or unused recovery code creates the real four-hour Admin session.
TOTP behavior:
- RFC 6238-compatible TOTP
- HMAC-SHA1, 6 digits, 30-second period
- accepts one adjacent time step for normal clock skew
- stores the last accepted TOTP step to reject code replay
- maximum five MFA attempts per challenge
Enrollment requires the Admin's current password. Setup returns the authenticator secret and eight one-time recovery codes once; the browser never persists either value. The authenticator secret is encrypted with AES-GCM before D1 storage. Recovery codes are normalized and stored only as hashes. No third-party QR-code service is used because sending the TOTP secret to such a service would expose the second-factor credential.
MFA endpoints:
POST /api/auth/mfa # second factor during sign-in
GET /api/mfa/status
POST /api/mfa/setup # current password required
POST /api/mfa/confirm # 6-digit TOTP required
POST /api/mfa/disable # current password + TOTP/recovery code; revokes all sessions
Recovery codes can be used for sign-in if the authenticator device is unavailable, including when the Worker encryption key is temporarily unavailable. Each recovery code is consumed atomically and can be used only once.
Role-based access control¶
Migration 0013_admin_rbac.sql adds admin_access. Existing Admin accounts are backfilled as
SuperAdmin so the production upgrade does not unexpectedly lock out current administrators.
Future Admin identities do not receive implicit business permissions merely from role='Admin'.
| Access level | Allowed workspace areas |
|---|---|
SuperAdmin |
Every Admin area plus Admin Access management |
Support |
Dashboard, Customers / Enquiries, private enquiry reference files |
Marketplace |
Dashboard, Freelancers, Customer Requirements |
Commerce |
Dashboard, Orders, Commerce Categories / Products / Images |
Operations |
Dashboard, Orders, Shipments |
The browser hides navigation that is not part of the assigned capability set, but the Worker is the authoritative boundary. Unknown/new protected API routes default to SuperAdmin-only.
SuperAdmin management endpoints:
The last remaining SuperAdmin cannot be demoted. Access changes are written to auth_audit and take
effect on the next protected API request.
There is still no public Admin registration flow.
Troubleshooting¶
- MFA setup says encryption is not configured: set
ADMIN_MFA_ENCRYPTION_KEYwithnpm run configure:admin:mfa-key. - Authenticator is unavailable: use one unused recovery code. Codes are single-use.
- A TOTP code says it was already used: wait for the next authenticator code; replay protection intentionally rejects the same time-step code twice.
- MFA challenge expired: repeat normal email/password sign-in; challenges last five minutes.
- Signed in but a section is hidden / returns 403: check
/api/rbac/meandadmin_access.access_level. - SuperAdmin cannot demote an account: the final SuperAdmin is intentionally protected from demotion.
- Cannot create the first Admin account: confirm
ADMIN_BOOTSTRAP_TOKENis configured. - Worker/static path problems:
run_worker_first: truemeans every Admin request passes through the Worker; usewrangler tail --config apps/admin-web/wrangler.jsonc.