#Security
#Authentication security
- Passport tokens: access 15 days, refresh 30 days, refresh rotates and revokes the old token. V8 requirement
- Passwords min 8 (V8). Reset logs out all devices. Password change logs out other devices. V8 requirement
- 2FA (email code 10 min / max 5 tries, TOTP, backup codes stored encrypted). Token only after 2FA. V8 requirement
- IP whitelist with emailed approval (24 h). V8 requirement
- Rate limits on register, login, 2FA, resends, forgot password, IP approval. V8 requirement
- One generic message for wrong email/password (no account enumeration). Specific messages only for unverified/banned after the password is correct Assumption (V7 checks status after the credentials).
#Session security (frontend)
- Tokens only in
httpOnly,secure,sameSite=Laxcookies. Never in localStorage, never in client bundles, never in URLs. - BFF route handlers attach the bearer server-side. CSRF: same-site cookies + checking
Originon mutating route handlers. ?next=redirects only to same-origin relative paths (no open redirect).- Logout clears cookies and all client stores.
#Authorization
- The backend checks membership + role permission + plan gating + subscription state on every organization-scoped request. The frontend only hides things.
- IDs from the URL are always looked up inside the user's organization (another organization's server → 404, not 403, so its existence isn't leaked).
- Owner-only areas (billing, plan, delete organization, remove member) are enforced server-side.
#Organization isolation
- Every query scoped by
organization_id. Policies on every model. Tests for cross-organization access on every endpoint (allow + deny), as the project's definition of done requires. - Server keys and provider tokens are bound to their organization. A proxy call can only use the key of a server in the caller's current organization.
#OAuth security
stateparameter (V7 base64-encodes JSON state; V8 should sign it and bind it to the session) Assumption.- Exact redirect URIs. Account linking by verified provider email only.
- Same 2FA/IP checks after OAuth. V8 requirement
#Payment verification
- Never trust the browser's return from the gateway. The backend verifies with the gateway API (and webhooks with signature verification).
- Idempotent crediting (a transaction key can credit once). Amounts come from the backend record, not the request.
- Pending transactions expire (V7 job every 30 min).
#Server key handling (sv_central_…)
- The key is a full admin credential for that server's OSS API (except user/role changes). Treat it like a root password. OSS API
- Stored encrypted at rest (Laravel encryption, APP_KEY per pair). Never returned by any API, never logged (V8 logging rule), never shown after entry (masked input; show only "connected" + last 4 if needed).
- Sent only from backend to the panel over HTTPS. Refuse
http://panel URLs Assumption. - SSRF: the panel URL is user input. Resolve DNS and refuse loopback/private/link-local/metadata addresses for every resolved IP, and pin the checked IP for the connection. (The OSS test round found a hostname bypass of exactly this check, so don't repeat it.)
- On 401 the server is marked
unauthorized, with no retry storms. - Disconnect deletes Central's copy. The user is told to revoke it on the panel too (Central can't).
- When Central generates a key for the installer (
CENTRAL_TOKEN), it uses a CSPRNG with the same length/format as OSS, and only[A-Za-z0-9_](the installer inserts it into a shell/PHP string).
#API security
- CORS: only the frontend origin,
api/*+oauth/*,Authorizationheader allowed, no wildcards. V8 requirement - HTTPS everywhere, trusted proxy configured, secure cookies. V8 requirement
- Validation in FormRequests. Mass-assignment protection. Pagination limits.
- Rate limits per user and per IP. OSS has its own limits, and the proxy must back off on 429.
#Sensitive data handling
| Data | Rule |
|---|---|
| Passwords | Hashed. Never logged |
| 2FA secrets, backup codes | Encrypted |
| Server keys, provider tokens, OAuth refresh tokens | Encrypted. Never returned. Never logged |
| Card data | Never touches Central (gateway-hosted fields / Stripe SetupIntent) |
OSS responses with secrets (DB passwords, .env values) | Passed to the browser only where the user has permission and the screen needs it. Never cached or logged by the proxy |
| Logs | Daily, 14 days, scrub tokens/passwords/Authorization. V8 requirement |
| Telescope | Local environment only. V8 requirement |
#Frontend vs backend authorization
The frontend permission map is a UX convenience. Any screen must keep working correctly when the backend returns 403 for something the UI thought was allowed.