Authentication
Overview
DocHub uses Google OAuth 2.0, restricted to @omelasai.com email accounts. Sessions are stored in PostgreSQL and shared with the CMS via a .ipnoelp.com cookie domain, so logging into either app authenticates you for both.
Auth Flow
- User visits a protected route
isAuthenticatedmiddleware checksreq.isAuthenticated()- If not authenticated: API routes get
401 JSON, page routes redirect to/auth/login - Login page shows a “Sign in with Google” button linking to
/auth/google - Passport initiates Google OAuth with
prompt: 'select_account' - Google redirects to
/auth/google/callback - Passport callback extracts email, checks
isAllowedDomain()againstALLOWED_EMAIL_DOMAINS - On success: user object serialized to session, redirect to
/ - On failure: redirect to login with error message
Dev Mode
When GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are not set (the default in .env), authentication is completely bypassed. The auth middleware is replaced with a passthrough:
const authMiddleware = authEnabled
? isAuthenticated
: (req: any, res: any, next: any) => next();
The /auth/me endpoint returns a synthetic local user:
{ "authenticated": true, "authEnabled": false, "user": { "id": "local", "email": "local@dev", "displayName": "Local Dev" } }
Session Store
Sessions use connect-pg-simple backed by the session table in the shared hypnoelp_cms PostgreSQL database. The table is created automatically if missing (createTableIfMissing: true).
Session cookie config:
secure: true in production (HTTPS only)httpOnly: true (no JS access)maxAge: 24 hoursdomain: set toCOOKIE_DOMAINenv var (.ipnoelp.comin production)
Shared Auth with CMS
Both the CMS (cms.ipnoelp.com) and DocHub (docs.ipnoelp.com) use:
- The same PostgreSQL
sessiontable - The same
SESSION_SECRET - The same
COOKIE_DOMAIN=.ipnoelp.com - The same Google OAuth client credentials
This means a session cookie set by the CMS is readable by DocHub and vice versa. Logging into the CMS at cms.ipnoelp.com gives you a valid session for docs.ipnoelp.com without re-authenticating.
Domain Restriction
isAllowedDomain() in middleware/auth.ts splits ALLOWED_EMAIL_DOMAINS on commas and checks the email’s domain against the list. Default: omelasai.com.
Files
| File | Purpose |
|---|---|
src/middleware/auth.ts |
isAuthenticated guard, isAllowedDomain helper (30 lines) |
src/routes/auth.ts |
OAuth routes, Passport config, login page HTML (168 lines) |