What this covers
Meridian’s first-party auth uses JWT access tokens plus refresh tokens backed by session records (when global identity is enabled). Users can sign in with email and password, Google, or Apple. SAML is a separate, school-configured path that still ends in the same global user + token model when multi-tenant identity is on. For deep dives, use the pages below; for architecture of global users, memberships, and SSO across subdomains, see Multi-tenant identity & SSO.Multi-tenant identity & SSO
Global users, tenant memberships, platform roles, and cookie domain SSO.
SAML (university SSO)
Per-school IdP config, callbacks, and coexistence with Google, Apple, and password login.
Sessions & refresh
Multi-device sessions, revoke flows, and how refresh ties to the global session store.
Supported sign-in methods
| Method | Typical use | Backend entry points |
|---|---|---|
| Email + password | Register and log in on the tenant (or www with school in the body) | POST /register, POST /login in Meridian/backend/routes/authRoutes.js |
| Web (auth code + PKCE), mobile / native (authorization code or ID token) | POST /google-login; token exchange and profile resolution in Meridian/backend/services/userServices.js (authenticateWithGoogle, authenticateWithGoogleIdToken) | |
| Apple | Mobile sends ID token; web can use Sign in with Apple callback | POST /apple-login, POST /auth/apple/callback; authenticateWithApple in userServices.js |
| SAML | Institution SSO per school | Meridian/backend/routes/samlRoutes.js and SAML |
Tenant context (req.school)
Auth routes resolve which MongoDB tenant to use from the host (subdomain), same as the rest of the API. On the www host, registration and social login require a school field in the JSON body so the backend can attach req.school and req.db to the correct tenant before creating or looking up users.
After a successful sign-in: global user, membership, tokens
When multi-tenant identity is in use (see Multi-tenant identity & SSO):GlobalUseris loaded or created from the tenant user / OAuth profile (Meridian/backend/services/authGlobalService.js—getOrCreateGlobalUser).TenantMembershiplinks that global identity to the current school and tenantUser(getOrCreateTenantMembership).- Platform roles (e.g. platform admin) are read from the global DB (
getPlatformRolesForGlobalUser). - Tokens are issued via
issueTokens: JWT access and refresh, with the refresh tied to a global session row for SSO-friendly refresh across subdomains.
completeLoginWithAdminMfa in authRoutes.js): the API returns requiresMfa and sets a short-lived pending cookie (or mfaToken for mobile) until TOTP or passkey verification completes.
Tokens and how clients send them
Access token
- Signed with
JWT_SECRET. - Payload includes
globalUserId, tenantroles, optionaltenantUserId,platformRoles, and MFA flags when applicable. Meridian/backend/middlewares/verifyToken.jsaccepts the token from:accessTokenHTTP-only cookie (browser), orAuthorization: Bearer <token>(API / mobile).
TOKEN_EXPIRED; clients should call POST /refresh-token (or re-login).
Refresh token and session
- Signed with
JWT_REFRESH_SECRETif set, otherwiseJWT_SECRET(seeissueTokensinauthGlobalService.js). - Web: stored in
refreshTokenHTTP-only cookie;POST /refresh-tokenandPOST /logoutread it from cookies. - Mobile: send
X-Client: mobileand pass the refresh token viaAuthorization: Bearer …orX-Refresh-Token(seeauthRoutes.jsrefresh and logout handlers). Successful login responses can returnaccessTokenandrefreshTokenin JSON for mobile.
ACCESS_TOKEN_EXPIRY / REFRESH_TOKEN_EXPIRY in authGlobalService.js (typically 15 minutes access, 30 days refresh unless overridden by env). Exact session fields and endpoints are documented under Session management.
Related HTTP endpoints (non-exhaustive)
All are mounted under the same auth prefix as the rest of your deployment (commonly/api/... — confirm in app.js).
| Area | Examples |
|---|---|
| Core auth | POST /login, POST /register, POST /refresh-token, POST /logout, GET /validate-token |
| OAuth | POST /google-login, POST /apple-login, POST /auth/apple/callback |
| Password recovery | POST /forgot-password, POST /verify-code, POST /reset-password |
| Email verification | POST /verify-email |
| Join another tenant while logged in | POST /join-tenant (requires valid access token) |
| Admin MFA | GET /mfa/admin/status, TOTP and passkey setup/verify routes under /mfa/... in authRoutes.js |
| Session UI | GET /sessions, DELETE /sessions/:sessionId, POST /sessions/revoke-all-others |
Environment variables (first-party auth)
Operators typically configure at least:JWT_SECRET(and optionallyJWT_REFRESH_SECRET)GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET(web/Android; iOS native Google clients use PKCE without the secret — seeauthenticateWithGoogleinuserServices.js)- Apple Sign In: keys and service configuration as required by
authenticateWithApple(see backend code and Apple’s docs)
How SAML fits in
SAML login is implemented insamlRoutes.js, not in the generic OAuth routes above. Once the IdP callback is processed, user provisioning and global user / membership alignment follow the same multi-tenant patterns as other methods where that stack is enabled. Details: SAML authentication.
Related pages
Backend best practices
req.db, req.globalDb, route layout, and how verifyToken resolves the tenant user.Testing
Backend route tests, multi-tenant scenarios, and CI coverage for auth-sensitive paths.
Multi-tenant test scenarios
Isolation checks and route-outcome patterns when identity spans tenants.
API reference
How clients call Meridian HTTP APIs with cookies or Bearer tokens.