Skip to main content

Customer Portal — Security Model

Status: populated Owner: Platform Engineering + Security Last updated: 2026-04-18

1. Threat Model Summary

The customer-portal is a browser-facing application. Primary threat vectors:

  • Session hijacking via XSS or cookie theft
  • CSRF attacks on state-changing operations
  • Credential exposure (raw API keys, signing secrets)
  • Broken access control (accessing other tenants' data)
  • Clickjacking

2. Authentication

MechanismDetail
Identity providerFirebase Auth (email/password)
Claim verificationcustomer custom claim verified by auth-service on login
Session storagehttpOnly; Secure; SameSite=Strict encrypted cookies
Token typeShort-lived JWT (1h) + 7-day rotating refresh token
Token locationServer-side only; never in localStorage or client JS
Firebase persistencebrowserSessionPersistence — no cross-tab persistence of Firebase token

3. JWT Handling

  • The raw platform JWT is stored in an encrypted httpOnly cookie.
  • Next.js server components and route handlers read the JWT from the cookie and attach it to upstream requests.
  • No JavaScript on the page can access __session cookie (HttpOnly).
  • JWT refresh happens server-side in middleware.ts; the client never handles tokens directly.

4. API Key Security

  • Raw API keys are generated and returned once by auth-service.
  • The portal receives the raw key in a single API response and displays it in a modal.
  • The raw key is held in React component state only, and never written to localStorage, sessionStorage, cookies, or server logs.
  • On modal close, the component state is cleared.
  • Key list never shows the raw key — only the keyId prefix and metadata.

5. CSRF Protection

All state-changing operations (POST, PUT, DELETE) are performed via Next.js route handlers. Protection:

  • SameSite=Strict on all cookies prevents cross-origin cookie submission.
  • Next.js route handlers verify the Origin header matches the portal origin.
  • An additional CSRF token (double-submit cookie pattern) is used for the test SMS and API key creation forms.

6. Content Security Policy

Production CSP header (set by Next.js custom headers() in next.config.ts):

Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{RANDOM}' https://www.gstatic.com https://apis.google.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.ghasi.io https://*.firebaseio.com wss://*.firebaseio.com;
frame-src 'none';
object-src 'none';
base-uri 'self';

7. Security Headers

HeaderValue
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preload
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
Referrer-Policystrict-origin-when-cross-origin
Permissions-Policycamera=(), microphone=(), geolocation=()

8. Access Control

  • The portal only calls Kong routes scoped to the authenticated account.
  • Kong injects X-Account-Id from the verified JWT; backend services filter all queries by this header.
  • The portal cannot access other accounts' data — no account ID is accepted from user input.

9. Secrets in Webhook Config

The webhook signingSecret is returned once at creation (same pattern as API keys). It is displayed in a modal and cleared from state on close. It is not stored by the portal.

10. Dependency Security

  • pnpm audit runs in CI; HIGH/CRITICAL vulnerabilities block merge.
  • Firebase client SDK loaded from the portal's own bundle — no CDN external script tag for Firebase.
  • No dangerouslySetInnerHTML usage except where content is sanitized with DOMPurify.