Skip to content

CMS Users And Web Users

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.

  • cms-customer operators sign in with native Payload CMS Users.
  • cms-leverage operators sign in with separate native Payload users.
  • Both CMS apps require verified email before login.
  • Better Auth owns web users and web sessions.
  • cms-customer shows Better Auth users through Web 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-customer and cms-leverage.
  • Customer environments initialize cms-customer only.
  • Leverage-owned environments initialize cms-leverage only. cms-leverage is never part of a customer-cluster deployment.

For each deployed CMS:

  1. Set its stable, non-placeholder CMS_CUSTOMER_PAYLOAD_SECRET or CMS_LEVERAGE_PAYLOAD_SECRET before the first login.
  2. Start the CMS.
  3. 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.
  4. Complete the first-user form as an admin. In cms-leverage, explicitly select Admin rather than Product Ops so user administration and recovery remain available.
  5. 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.

Before a production CMS is handed to operators:

  • Keep PAYLOAD_DISABLE_EMAIL=false and PAYLOAD_ALLOW_EMAIL_DISABLE_IN_PRODUCTION=false for 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, and SMTP_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.

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.
Terminal window
bun run agent-browser:login:web
bun run agent-browser:login:cms-customer
bun run agent-browser:login:cms-leverage
  • Web login uses Better Auth credentials.
  • Customer CMS login uses AGENT_BROWSER_CMS_CUSTOMER_EMAIL and AGENT_BROWSER_CMS_CUSTOMER_PASSWORD.
  • Leverage CMS login uses AGENT_BROWSER_CMS_LEVERAGE_EMAIL and AGENT_BROWSER_CMS_LEVERAGE_PASSWORD.
  • The CMS users must already be verified in Payload.
  • The helper parses AGENT_BROWSER_*_EMAIL and AGENT_BROWSER_*_PASSWORD with dotenv, so literal $ characters in passwords are preserved.

Default sessions:

  • web: leverage-web
  • customer CMS: leverage-cms-customer
  • leverage CMS: leverage-cms

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.

Terminal window
bun run --cwd apps/auth repair:web-user-projection
bun run --cwd apps/auth repair:web-user-projection --apply

Run 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 Requests cannot 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.
Terminal window
bun run test:cms-customer:email:e2e

This 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 Request sends the canonical Auth completion link.
  • The requester completes setup through Web, signs in, and appears in Web App Users as 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.

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