Customer Portal — Data Model
Status: populated Owner: Product Engineering (Frontend) Last updated: 2026-04-18
1. No Database
The customer-portal is a stateless frontend service. It owns no database schema, no PostgreSQL tables, and no Redis keys. All persistent data is owned by upstream backend services consumed via Kong.
2. Server-Side Session (Cookie-Based)
The portal uses Next.js iron-session (or equivalent encrypted cookie library) to store session data server-side without a session store.
__session Cookie
- Type: Encrypted, serialized JSON
- Flags:
HttpOnly; Secure; SameSite=Strict; Path=/ - TTL: Matches JWT
exp(1 hour); renewed on refresh - Contents:
{
"userId": "usr_01H...",
"accountId": "acc_01H...",
"email": "customer@example.com",
"roles": ["customer"],
"accessToken": "<platform-jwt>",
"expiresAt": "2026-04-18T15:00:00Z"
}
__refresh Cookie
- Type: Encrypted, serialized JSON
- Flags:
HttpOnly; Secure; SameSite=Strict; Path=/api/auth/refresh - TTL: 7 days
- Contents:
{
"refreshToken": "<opaque-refresh-token>"
}
The Path restriction ensures the refresh token cookie is only sent to the refresh endpoint, not all API routes.
3. Session Storage
| Key | Type | Content | Cleared |
|---|---|---|---|
cust_msg_filters | JSON string | Last-used message filter state { from, to, status, startDate, endDate } | On tab close |
4. In-Memory React State
No persistent client state. All server data is fetched fresh on navigation. Ephemeral component state:
| Component | State | Description |
|---|---|---|
ApiKeyCreateModal | rawKey: string | null | Raw key string held only during "key created" display; nulled on close |
WebhookCreateModal | signingSecret: string | null | Signing secret held only during "webhook created" display |
TestSmsForm | formState | react-hook-form field values |
MessageFilters | URL search params | Serialised to URL; not in component state |
5. No localStorage for Sensitive Data
The portal explicitly avoids writing tokens or credentials to localStorage or sessionStorage. Firebase Auth's IndexedDB-based persistence is set to browserSessionPersistence to prevent cross-tab token leakage.
6. Cache Headers
Next.js server component responses:
/dashboard:Cache-Control: private, no-store— always fresh/messages:Cache-Control: private, no-store- Static assets (
/_next/static):Cache-Control: public, max-age=31536000, immutable