PageSpace implements a zero-trust security model where every request is authenticated and authorized independently. No request is trusted based on network location or prior authentication alone.
PageSpace migrated from JWTs to opaque session tokens for several security advantages:
| Concern | JWT | Opaque Token |
|---|---|---|
| Payload exposure | Token contains user data | Token is a random string |
| Revocation | Requires blocklist or short expiry | Instant — delete from database |
| Token size | Large (1KB+) | Small (~60 bytes) |
| Validation | Cryptographic signature check | Hash lookup in database |
| Stolen token | Valid until expiry | Revocable immediately |
ps_sess_<32 bytes of randomness, base64url encoded>
ps_sess_, ps_dev_, ps_sock_, ps_unsub_)Internal services (web, realtime, processor) authenticate with each other using shared secrets:
Web App ←→ Realtime Service (Socket.IO)
Web App ←→ Processor Service (File Processing)
The tokenVersion field enables instant invalidation of all sessions:
Password change → Increment tokenVersion → All existing sessions invalid
"Logout all devices" → Increment tokenVersion → All sessions invalid
Every token validation checks the user's tokenVersion. If the stored version doesn't match, the session is rejected.
| Endpoint | Strategy | Purpose |
|---|---|---|
| Login | Per-IP + per-email | Prevent brute force |
| Signup | Per-IP | Prevent mass account creation |
| Token refresh | Per-user | Prevent token abuse |
| API routes | Per-user | Prevent API abuse |
Rate limits use a sliding window algorithm. Exceeding the limit returns HTTP 429 with a Retry-After header. Successful authentication resets the counter.
| Event | Data Captured |
|---|---|
| Login attempt | Email, IP, user agent, success/failure |
| Signup | Email, IP, provider (email/Google) |
| Password change | User ID, IP |
| Token refresh | User ID, device, IP |
| Permission change | Granter, grantee, page, flags |
| MCP token usage | Token ID, operation, timestamp |
| AI tool execution | User, tool name, page context, success |
State-changing requests require a CSRF token:
GET /api/auth/csrfUser-generated HTML content is sanitized to prevent XSS:
AI provider API keys are encrypted at rest in the database. Keys are:
Search docs, blog posts, and more.