Platform auth integration

MVS operator apps (the platform-admin surface and its kin) authenticate against a separate auth service from tenant users. It is live at https://c2-auth.mvscloud.com and serves a Better Auth instance with its own session, distinct from central tenant auth at c-auth.mvscloud.com.

Two surfaces, two sessions. Tenant users authenticate at c-auth.mvscloud.com (see the OIDC integration guide); MVS operators authenticate at c2-auth.mvscloud.com. They share the same registrable domain for cookie scoping but are independent Better Auth instances with separate BETTER_AUTH_SECRETs.

1. The model — redirect, don’t fetch

c2-auth.mvscloud.com is a dedicated Vercel project (owned by mvs-auth) that hosts the platform-auth Better Auth handler directly at /api/platform-auth/*. An operator app does not stand up its own auth handler and does not make a cross-origin auth fetch. Instead:

  1. The operator app (e.g. c2.mvscloud.com) redirects the operator to https://c2-auth.mvscloud.com/login to authenticate.
  2. Platform auth signs them in (Okta SSO is the primary path — see the Okta SSO setup runbook).
  3. The session cookie is minted with Domain=mvscloud.com (cross-subdomain), so the operator app reads the shared .mvscloud.com session cookie directly — no token exchange, no cross-origin call.
Operator browser
│ navigate → https://c2.mvscloud.com (operator app)
│ redirect → https://c2-auth.mvscloud.com/login
▼ sign in (Okta SSO / passkey)
c2-auth.mvscloud.com (apps/platform-auth — Better Auth instance)
│ Set-Cookie: Domain=mvscloud.com (shared across *.mvscloud.com)
Operator app reads the shared session cookie — no cross-origin auth fetch.
ConcernValue
Cookie domainmvscloud.com (cross-subdomain; shared across *.mvscloud.com)
Hostc2-auth.mvscloud.com (CNAME → the platform-auth Vercel deployment)
Handler base path/api/platform-auth
Session storeDB-authoritative in the isolated mvs-auth-db (Redis, when present, only accelerates reads)

The platform-auth base URL must resolve to a mvscloud.com host. In production PLATFORM_AUTH_BASE_URL is left unset so the code defaults to https://c2-auth.mvscloud.com. Never point it at a *.vercel.app deployment URL: the browser rejects a Domain=mvscloud.com cookie on a vercel.app origin (sign-in fails outright), and the rate-limiter treats a vercel.app host as non-production and silently disables itself.

3. Trusted origins

Platform auth trusts the MVS estate’s registrable domains for its CSRF / origin checks — any *.mvscloud.com subdomain is trusted. An operator app served from a mvscloud.com subdomain needs no special configuration; the shared-cookie redirect model works out of the box. If your operator surface lives on a different registrable domain, it must be added to the trusted-origins allowlist server-side — coordinate with the operator (it is not self-service).

Operator-auth enforcement (PLATFORM_SSO_ONLY) — zero-password, Okta-SSO + passkey-only — is coded but not yet enforced in production (pending Okta config and operator passkey enrollment). Until then, password login is temporarily on. See the Operations page for the enforcement levers.

4. Client library

In-monorepo operator apps use the platform variant of the auth client:

1import { createPlatformAuthClient } from "@mvs/auth-client";
2
3// Uses basePath (default /api/platform-auth) against c2-auth.mvscloud.com —
4// NOT a baseUrl. It omits organizationClient/customSessionClient; use the
5// central client for org/session actions.
6const platformAuthClient = createPlatformAuthClient();

This wraps Better Auth’s React client against the platform-auth host. It is for operator session/cookie auth on *.mvscloud.com — it is not an OIDC genericOAuth client.

5. OIDC for operator apps

Beyond the shared session cookie above, c2-auth.mvscloud.com is now a full OIDC provider (an operator IdP). Operator apps can register a platform_oauth_client (the isolated platform table, keyed to PlatformUsernot central’s oauth_client) and run the same authorization-code + PKCE flow tenant apps use at c-auth.mvscloud.com. Use this when an operator app runs its own Better Auth instance and consumes c2-auth as an upstream OIDC provider, rather than reading the shared cookie directly.

This is shipped and live in production. The platform IdP is a second, isolated OIDC provider (its own platform_oauth_* tables and platform_jwks, keyed to PlatformUser). Register operator-app clients via the self-service client registration API/IAT flow (platform surface) or the operator CLI — see the Operator-app OIDC integration guide for the full walkthrough: discovery, the genericOAuth provider config (providerId: "c2-auth"), redirect URIs, claims, and token verification.