Application authentication
MAIL-73 adds the Inbox Agent application principal. This is not Microsoft Graph or Gmail OAuth. Provider tokens stay out of the app session and are MAIL-74's responsibility.
See ADR 0005.
User
Migration 0002_users.sql creates inbox_agent.users:
| Column | Notes |
|---|---|
user_id | UUID primary key |
identity_subject | Unique local subject (not a provider mailbox id) |
status | active or disabled |
created_at / updated_at | Timestamps |
Row-level security is enabled. anon and authenticated are revoked when those roles exist. The table is not on the Supabase Data API.
Repository: createUserRepository in @inbox-agent/persistence.
Session
SESSION_SECRET (32+ characters, already required by loadRequiredEnv) signs an HTTP-only cookie named ia_session.
Cookie flags: HttpOnly, Path=/, SameSite=Lax, Max-Age 12 hours. Secure is set when the issuer asks for a secure cookie (preview and production).
Claims allowlist:
v, userId, iat, exp, csrf
The public session view returned to API clients is { userId, csrfToken } only.
CSRF
Mutating methods (POST, PUT, PATCH, DELETE) must send X-CSRF-Token matching the csrf claim inside the signed cookie. GET/HEAD/OPTIONS skip CSRF but still require a valid session.
API contract
Every user-facing API call goes through authenticateRequest / handleProtectedApi. Missing or invalid cookies return:
{
"data": null,
"error": {
"code": "UNAUTHENTICATED",
"message": "Authentication required",
"retryable": false
}
}
CSRF failures return 403 with UNAUTHORIZED. Neither response includes provider tokens.
Logging
Reuse MAIL-69 redaction. cookie, token, and authorization field names are already redacted by @inbox-agent/security. Do not add a second redaction implementation.
