Auth
Shipmore Core uses a dual auth model: Payload’s built-in auth for the admin panel, and Better Auth for frontend customer sign-in and sessions.
Overview
| Concern | System | Collection | URL |
|---|---|---|---|
| Admin login | Payload auth | users | /admin |
| Customer sign-in | Better Auth | customers | /auth (per tenant) |
Admin users manage tenants, pages, and products. Customers sign in on the frontend to access dashboards, make purchases, and manage subscriptions.
Better Auth setup
Better Auth is integrated via the payload-auth plugin, which bridges Better Auth with Payload’s database and collections.
Providers
Out of the box, three sign-in methods are configured:
- Email + password — standard credentials-based sign-in.
- Magic link — passwordless sign-in via email link.
- Google OAuth — social sign-in with Google.
Configuration
Auth options live in src/domains/auth/lib/options.ts. The key settings:
baseURL: undefined— Better Auth infers the base URL from each request’s host. This is critical for multi-tenant setups where each tenant has a different domain.- Session — cookie-based with a 5-minute cache for performance.
- Account linking — enabled for Google and email-password. If a user signs in with Google and later with email using the same address, the accounts are linked.
Google OAuth
To enable Google sign-in:
- Go to the Google Cloud Console → APIs & Services → Credentials.
- Create an OAuth 2.0 Client ID (Web application type).
- Add Authorized redirect URIs for each tenant domain, e.g.:
http://localhost:3000/api/auth/callback/google(local dev)https://mysite.com/api/auth/callback/google(production)- Add one redirect URI per tenant domain you want Google sign-in on.
- Set the env vars:
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
NEXT_PUBLIC_GOOGLE_CLIENT_ID=your-client-id # used client-sideIf these vars are not set, the Google sign-in button won’t appear.
Multi-tenant auth
Trusted origins
Better Auth needs to trust each tenant domain for CORS and cookie handling. The configuration automatically adds:
- Origins from env vars (
NEXT_PUBLIC_BETTER_AUTH_URL,BETTER_AUTH_URL,NEXT_PUBLIC_SERVER_URL). - The current request’s host — so any tenant domain is automatically trusted at runtime.
OAuth callbacks per tenant
For social sign-in (Google), the OAuth callback URL must match the domain the user is signing in from. A middleware in the auth configuration overrides the baseURL for /sign-in/social and /callback/* routes using the request’s Host header. This ensures the OAuth flow redirects back to the correct tenant domain.
In practice, this means Google OAuth works on any tenant domain as long as you’ve added the redirect URI for that domain in the Google Cloud Console.
Cookie scoping
Auth cookies are scoped to the domain they were set on. A customer signed in on tenant-a.com is not signed in on tenant-b.com. This is expected browser behavior and provides proper tenant isolation.
Session handling
Server-side
In server components, API routes, and server actions, read the session from request headers:
import { getSession } from '@/domains/auth/lib/session'
const session = await getSession(headers)
// session.user contains the authenticated customerUnder the hood, this calls payload.betterAuth.api.getSession({ headers }).
Client-side
In client components, use the auth client:
import { createAuthClient } from '@/domains/auth/lib/client'
const authClient = createAuthClient()
// authClient.useSession(), authClient.signIn.email(), etc.The client uses window.location.origin as its base URL, so it automatically points to the current tenant’s domain.
Protected routes
Server actions use zsa procedures for route protection:
authProcedure— requires an active session. Returns 401 if unauthenticated.stripeCustomerProcedure— extendsauthProcedureand ensures the customer has a Stripe customer record.paywallProcedure— extends further with paywall checks (credits, subscription tier).
Frontend pages like /dashboard check the session at the page or layout level and redirect unauthenticated users to /auth.
Related pages
- Monetization & Payments — Stripe and checkout
- Security — access control and session security details