CMS Users And Web Users
What This Covers
Section titled “What This Covers”Use this page when you are bootstrapping the first CMS user, debugging customer CMS or Leverage CMS login, reviewing web access requests, or inspecting the read-only Web App Users mirror.
Current Model
Section titled “Current Model”cms-customeroperators sign in with native PayloadCMS Users.cms-leverageoperators sign in with separate native Payload users.- Both CMS apps require verified email before login.
- Better Auth owns web users and web sessions.
cms-customershows Better Auth users throughWeb App Users, a read-only Payload mirror.
Initial User Bootstrap In Every Environment
Section titled “Initial User Bootstrap In Every Environment”Each CMS needs one initial native Payload admin account when it starts against a new or cleared database. This applies to local, test, staging, production, and recovery environments. The bootstrap belongs to the database, not the application deployment: do not create another initial account after an ordinary restart or redeploy that keeps the same database.
Initialize only the CMS apps deployed in that environment:
- Local development normally runs and initializes both
cms-customerandcms-leverage. - Customer environments initialize
cms-customeronly. - Leverage-owned environments initialize
cms-leverageonly.cms-leverageis never part of a customer-cluster deployment.
For each deployed CMS:
- Set its stable, non-placeholder
CMS_CUSTOMER_PAYLOAD_SECRETorCMS_LEVERAGE_PAYLOAD_SECRETbefore the first login. - Start the CMS.
- From a trusted operator session or network, open the configured CMS base URL with
/admin. Payload redirects an empty database to/admin/create-first-user. - Complete the first-user form as an admin. In
cms-leverage, explicitly selectAdminrather thanProduct Opsso user administration and recovery remain available. - Confirm that the initial admin can log in before handing the environment to operators.
Payload auto-verifies this first account when no users exist. Users created later must complete email verification before login. The unauthenticated first-user route is available only while that CMS has no users.
Production Handoff And Email Smoke
Section titled “Production Handoff And Email Smoke”Before a production CMS is handed to operators:
- Keep
PAYLOAD_DISABLE_EMAIL=falseandPAYLOAD_ALLOW_EMAIL_DISABLE_IN_PRODUCTION=falsefor normal runtime. - Configure provider-backed SMTP values:
EMAIL_TRANSPORT_MODE=smtp,EMAIL_FROM_ADDRESS,EMAIL_FROM_NAME,SMTP_HOST,SMTP_PORT,SMTP_SECURE,SMTP_USER, andSMTP_PASS. - Start the CMS and confirm it fails closed if SMTP config is incomplete.
- Create a second CMS admin before go-live and verify that the second admin receives the Payload verification email, completes verification, and can log in.
- Confirm forgot-password email delivery for at least one verified CMS operator.
- Restart the CMS with the same Payload secret and confirm an unexpired CMS browser session remains authenticated.
Seed and maintenance commands may set PAYLOAD_DISABLE_EMAIL=true together with PAYLOAD_ALLOW_EMAIL_DISABLE_IN_PRODUCTION=true when they intentionally load Payload without sending email. Do not put that bypass in normal production runtime env.
What You Can Edit
Section titled “What You Can Edit”| Surface | Editable? | Notes |
|---|---|---|
CMS Users in cms-customer |
Yes | Native Payload customer CMS operators. Initial role set is admin. |
Users in cms-leverage |
Yes | Native Payload Leverage CMS operators. Only Leverage CMS admins can administer users; non-admin roles use explicitly gated workflows. |
Web App Users in cms-customer |
No | Better Auth mirror for lookup and support diagnostics only. |
Web Access Requests in cms-customer |
Limited | Create sends an Auth-backed completion invitation; approve/reject/regenerate/resend/spam cleanup use Auth-owned actions. Direct delete is disabled. |
Local Browser Login
Section titled “Local Browser Login”bun run agent-browser:login:webbun run agent-browser:login:cms-customerbun run agent-browser:login:cms-leverage- Web login uses Better Auth credentials.
- Customer CMS login uses
AGENT_BROWSER_CMS_CUSTOMER_EMAILandAGENT_BROWSER_CMS_CUSTOMER_PASSWORD. - Leverage CMS login uses
AGENT_BROWSER_CMS_LEVERAGE_EMAILandAGENT_BROWSER_CMS_LEVERAGE_PASSWORD. - The CMS users must already be verified in Payload.
- The helper parses
AGENT_BROWSER_*_EMAILandAGENT_BROWSER_*_PASSWORDwithdotenv, so literal$characters in passwords are preserved.
Default sessions:
- web:
leverage-web - customer CMS:
leverage-cms-customer - leverage CMS:
leverage-cms
Repairing The Web App Users Mirror
Section titled “Repairing The Web App Users Mirror”The repair command is safe to run in production in dry-run mode. It reports intended changes without writing. Use the explicit apply flag only after reviewing the dry-run output.
bun run --cwd apps/auth repair:web-user-projectionbun run --cwd apps/auth repair:web-user-projection --applyRun it while cms-customer is reachable with matching CMS_CUSTOMER_BASE_URL and CMS_CUSTOMER_SERVICE_API_KEY.
Current limits:
- Repair performs idempotent upserts.
- Stale mirror rows are reported.
- Deletes are not performed by default.
- Generic Better Auth user lifecycle sync is best-effort; access-request completion and the repair command are the stronger reconciliation paths.
- Blocking, revoking, or editing Better Auth web users from CMS is not implemented.
Web Access Requestscannot be directly deleted from Payload. If a bad pending request exists, use reject where applicable. If a request is spam, use the CMS spam cleanup action; it calls Auth to invalidate active completion artifacts before the mirrored row is removed. CAPTCHA-style request-access UI is deferred, but Auth still rate-limits repeated public submissions by email/client window.
Local Email E2E
Section titled “Local Email E2E”bun run test:cms-customer:email:e2eThis starts isolated Mongo, Auth, Web, cms-customer, and a local SMTP capture server. It verifies:
- Creating a customer CMS user sends a native Payload verification link.
- The unverified CMS user cannot log in before using that link.
- A CMS-created
Web Access Requestsends the canonical Auth completion link. - The requester completes setup through Web, signs in, and appears in
Web App Usersas an active verified mirror record.
This proves the app-owned SMTP/link lifecycle without depending on a hosted inbox. It does not prove real inbox placement, sender reputation, SPF/DKIM/DMARC, or provider-specific deliverability.
Canonical Sources
Section titled “Canonical Sources”| Source | Why you would open it |
|---|---|
docs/features/authentication/spec.md |
Auth authority, access-request, mirror, and CMS auth requirements |
docs/features/authentication/decisions/ADR-010-better-auth-admin-plugin-and-payload-access-control.md |
Decision to use native CMS users and a read-only Better Auth web-user mirror |
docs/features/authentication/decisions/ADR-011-access-request-mirror-and-auth-authority.md |
Why web access requests remain Auth-owned |
LIMITATIONS.md |
Current mirror and Payload verification limitations |
apps/cms-customer/README-auth-integration.md |
Customer CMS implementation-level auth notes |
apps/cms-customer/src/collections/Users.ts |
Customer CMS native user roles and verification behavior |
apps/cms-leverage/src/collections/Users.ts |
Leverage CMS native user roles and admin-only user management |
apps/cms-customer/src/payload.config.ts |
Customer CMS Payload user and database configuration |
apps/cms-leverage/src/payload.config.ts |
Leverage CMS Payload user and database configuration |