Security & Trust Model
Authentication, authorization, internal trust, and purchase flow hardening.
AnyaSelf handles family finances (purchase approvals, checkout confirmation) and personal data (wardrobe, style preferences). This page documents the security architecture.
Authentication Architecture
graph LR
subgraph Client
APP["React SPA"]
end
subgraph External
OIDC["OIDC Provider<br/>(Firebase Auth / Google)"]
end
subgraph AnyaSelf
GW["API Gateway :8080"]
SERVICES["Downstream Services"]
end
APP -- "1. Login via provider SDK" --> OIDC
OIDC -- "2. ID Token" --> APP
APP -- "3. POST /auth/login { idToken }" --> GW
GW -- "4. Verify token via JWKS" --> OIDC
GW -- "5. Mint internal JWT" --> APP
APP -- "6. Bearer {internalJWT}" --> GW
GW -- "7. Forward principal" --> SERVICESJWT Model
| Environment | Algorithm | Secret Source |
|---|---|---|
| Development | HS256 | Shared AUTH_JWT_SECRET (default: dev-secret-change-me) |
| Production | RS256 | OIDC/JWKS from AUTH_EXTERNAL_JWKS_URL |
In production, the default dev-secret-change-me is rejected at startup to prevent accidental deployment with weak credentials.
Token Lifecycle
- Client authenticates with an external OIDC provider (Firebase Auth, Google)
- Client sends the external
idTokentoPOST /auth/login - Gateway verifies the token against the provider's JWKS endpoint
- Gateway mints an internal AnyaSelf bearer token with
AUTH_ACCESS_TOKEN_TTL_SECONDS(default: 3600s) - Internal token contains
sub(userId) andemailclaims
Internal Trust Model
Microservices authenticate machine-to-machine calls using shared X-Internal-Token headers.
| Token | Services | Purpose |
|---|---|---|
ORCHESTRATOR_INTERNAL_TOKEN | orchestrator ↔ hyperbeam-bridge, orchestrator ↔ artifacts-audit | Bridge events, artifact writes |
BUYFLOW_INTERNAL_TOKEN | orchestrator → api-gateway | cart-ready and failed purchase transitions |
[!WARNING] In production, set
REQUIRE_INTERNAL_EVENT_TOKEN=trueandREQUIRE_BUYFLOW_INTERNAL_TOKEN=true. Without these, internal endpoints are unprotected.
Purchase Flow Hardening
The buy-flow is the most security-critical path because it involves real money.
sequenceDiagram
participant U as User
participant GW as API Gateway
U->>GW: POST /requests (create)
Note over GW: Status: SUBMITTED
U->>GW: POST /requests/{id}/approve
Note over GW: Status: APPROVED
U->>GW: POST /requests/{id}/purchase-intents
Note over GW: Mint ephemeral confirmationToken<br/>TTL: 900s (CHECKOUT_CONFIRMATION_TTL_SECONDS)
Note over U: confirmationToken held in memory only<br/>NEVER in localStorage/sessionStorage
U->>GW: POST /requests/{id}/confirm-checkout<br/>{intentId, confirmationToken}
Note over GW: Verify token + TTL → Status: COMPLETEDKey Safeguards
- Actor binding:
cart-readyandfailedtransitions are bound to the user who created the purchase intent - Ephemeral confirmation token: Minted on intent creation, valid for
CHECKOUT_CONFIRMATION_TTL_SECONDS(default 900s) - Memory-only token storage: Frontend must never persist the confirmation token to
localStorageorsessionStorage - Internal token gate:
cart-readyandfailedendpoints requireX-Internal-TokenwhenREQUIRE_BUYFLOW_INTERNAL_TOKEN=true
Data Scoping
All AnyaSelf data is scoped to the Household resource:
- Every API path includes
{householdId}as a path parameter - JWT principal must be a member of the household to access its data
- Household deletion (
DELETE /households/{id}) cascades to all members, items, outfits, requests, and audit logs
Rate Limiting
| Endpoint | Limit | Window |
|---|---|---|
POST /voice/live/public-session | VOICE_LIVE_PUBLIC_RATE_LIMIT_MAX_REQUESTS (default: 12) | VOICE_LIVE_PUBLIC_RATE_LIMIT_WINDOW_SECONDS (default: 60s) |
Rate limiting is IP-based, resolved via X-Forwarded-For header.
Hyperbeam Event Signatures
When HYPERBEAM_ENFORCE_EVENT_SIGNATURES=true, the Chrome Extension running inside Hyperbeam sessions must sign events with an HMAC using HYPERBEAM_EVENT_SIGNING_SECRET. Unsigned or incorrectly signed events are rejected.