docs: define merchant session baseline
This commit is contained in:
130
docs/IDENTITY-SPEC.md
Normal file
130
docs/IDENTITY-SPEC.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Identity and Access Capability Spec
|
||||
|
||||
- Status: Draft — not approved for implementation
|
||||
- Capability: Identity and access
|
||||
- Depends on: Approved Data Foundation specification
|
||||
- Last updated: 2026-08-20
|
||||
|
||||
## Objective
|
||||
|
||||
Allow hackathon judges and merchants to authenticate as distinct merchant users, while enforcing strict server-side tenant isolation. Seed one demo user per merchant through a secure one-time administrative workflow so different merchant dashboards can be demonstrated without adding self-service account management to the MVP.
|
||||
|
||||
## Actors
|
||||
|
||||
- Demo merchant user: authenticates with assigned credentials and receives access to exactly one merchant.
|
||||
- Merchant user: the same MVP authorization model as a demo user; roles and delegated access are not yet defined.
|
||||
- Operator: runs the one-time credential-generation workflow and distributes credentials outside Git.
|
||||
- Unauthenticated visitor: may access only the login flow and public health/static resources explicitly allowlisted by later API/deployment specs.
|
||||
|
||||
## In scope
|
||||
|
||||
- Username/password authentication for one seeded user per merchant
|
||||
- Argon2id password hashing
|
||||
- Server-side, revocable authenticated sessions
|
||||
- Secure cookie transport and logout
|
||||
- Idle and absolute session expiry
|
||||
- Account disablement and session revocation
|
||||
- Generic authentication failures
|
||||
- Tenant identity derived exclusively from the authenticated session
|
||||
- One-time demo-credential generation and secure one-time display
|
||||
- Security-event audit records that contain no credentials or raw session tokens
|
||||
|
||||
## Out of scope for MVP
|
||||
|
||||
- Self-registration
|
||||
- Password reset/recovery
|
||||
- Email or phone verification
|
||||
- Multi-factor authentication
|
||||
- Social login or SSO
|
||||
- User-created roles, permissions, or additional merchant users
|
||||
- Cross-merchant operator access in the browser
|
||||
- Storing credentials, session tokens, or generated credential exports in Git
|
||||
|
||||
## Domain and data contract
|
||||
|
||||
- A `Merchant User` belongs to exactly one merchant in the MVP.
|
||||
- Usernames are unique after the approved normalization rule; the exact normalization remains open.
|
||||
- Passwords are stored only as Argon2id encoded hashes with a unique salt and reviewed, versioned parameters. Plaintext passwords are accepted only at the login boundary or generated by the one-time credential workflow, then discarded.
|
||||
- An `Authenticated Session` belongs to one merchant user and records creation, last activity, idle expiry, absolute expiry, revocation, and minimal security provenance required by the approved retention policy.
|
||||
- The browser receives only a cryptographically random opaque session token. The server stores a non-reversible digest of that token, never the raw token.
|
||||
- Every merchant-scoped backend operation resolves merchant identity from the authenticated session. Client-supplied merchant identifiers may filter only within that boundary and never grant access.
|
||||
|
||||
## Authentication and session contract
|
||||
|
||||
1. Successful login rotates into a new server-side session and sets one cookie with `Secure`, `HttpOnly`, and `SameSite=Lax` attributes. Authentication tokens are never placed in `localStorage`, `sessionStorage`, URLs, or frontend-readable state.
|
||||
2. The cookie is host-only by default, uses the narrowest practical path, and is transmitted only over HTTPS in deployed environments. Local development behavior must be explicit without weakening production defaults.
|
||||
3. A session expires after 12 hours without approved activity and always expires seven days after creation. Activity cannot extend the absolute deadline.
|
||||
4. Logout revokes the server-side session before clearing the browser cookie. Account disablement revokes all active sessions for that user immediately.
|
||||
5. Invalid username, invalid password, disabled account, and other expected login failures return the same user-facing message and externally equivalent response shape.
|
||||
6. Authentication and authorization failures never reveal password hashes, raw tokens, user existence, merchant existence, stack traces, or internal policy details.
|
||||
7. Protected HTTP and SSE requests use the same authenticated merchant boundary. SSE replay or reconnect never survives session expiry or revocation.
|
||||
|
||||
## Demo credential workflow
|
||||
|
||||
1. A one-time administrative command creates exactly one initial demo user for each merchant that does not already have one.
|
||||
2. Usernames and random passwords are generated independently from source merchant keys and payment data.
|
||||
3. Re-running the workflow is idempotent: it does not silently replace existing credentials or create duplicates.
|
||||
4. New plaintext credentials are displayed or exported once through an explicitly selected secure destination, with restrictive filesystem permissions where a file is requested.
|
||||
5. The workflow never writes plaintext credentials to application logs, import reports, source-controlled files, shell history by default, or database columns.
|
||||
6. Lost demo credentials are replaced only through a separate explicit operator action whose revocation and audit behavior must be specified before implementation.
|
||||
|
||||
## Threat model and abuse cases
|
||||
|
||||
| Boundary/asset | Main abuse case | Required control |
|
||||
|---|---|---|
|
||||
| Login endpoint / credentials | Enumeration or credential guessing | Generic failures; login throttling policy still requires approval |
|
||||
| Browser / session token | Token theft through script or transport | `HttpOnly`, `Secure`, host-only cookie; HTTPS; no browser storage |
|
||||
| Session store | Stolen database reveals usable tokens | Store only token digests; expire and revoke server-side |
|
||||
| Merchant API / tenant data | User changes a merchant id to read another tenant | Derive authorization scope from session on every operation |
|
||||
| Demo credential generation | Plaintext secrets leak into Git/logs/history | Random generation, one-time output, explicit secure destination, secret scanning |
|
||||
| SSE reconnect/replay | Revoked user continues receiving events | Reauthorize every connection and replay request |
|
||||
|
||||
## Failure behavior
|
||||
|
||||
- Missing, malformed, expired, or revoked sessions receive the same typed unauthenticated response and trigger no merchant data access.
|
||||
- Authenticated users requesting a resource outside their merchant boundary receive a typed forbidden/not-found policy defined by the API spec without confirming another merchant's resource exists.
|
||||
- Session-store failure fails closed: protected requests do not proceed using unverifiable client state.
|
||||
- Credential-generation partial failure reports affected merchant identifiers without printing generated passwords again or leaving an apparently complete run.
|
||||
|
||||
## Observability and audit
|
||||
|
||||
- Record successful login, failed login category without username-existence disclosure, logout, session revocation, account disablement, and credential-generation operator actions.
|
||||
- Logs and metrics exclude passwords, password hashes, raw session tokens, cookie values, and sensitive payment data.
|
||||
- Security-event retention, IP/user-agent treatment, and privacy minimization remain open decisions.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- At least two seeded merchant users can authenticate and each sees only its own merchant data.
|
||||
- Password database values are Argon2id hashes; plaintext credentials are absent from database, logs, Git, and normal command history.
|
||||
- The browser contains only a `Secure`, `HttpOnly`, `SameSite=Lax` opaque session cookie and no authentication token in frontend-readable storage.
|
||||
- Raw session tokens are absent from server storage; only non-reversible digests are persisted.
|
||||
- Idle sessions expire after 12 hours, all sessions expire within seven days, and activity never extends the absolute limit.
|
||||
- Logout and account disablement make an existing cookie unusable immediately, including for SSE reconnect/replay.
|
||||
- Invalid username, password, and disabled-account attempts are externally indistinguishable in user-facing content.
|
||||
- Modifying a merchant identifier cannot cross the merchant boundary enforced by the authenticated session.
|
||||
- The demo-user workflow creates missing users idempotently, displays new credentials once, and never commits or logs them.
|
||||
- Security tests cover enumeration resistance, session fixation/rotation, expiry, revocation, cross-tenant access, cookie flags, token-digest storage, and credential-output safety.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Always: validate inputs, use parameterized persistence, rotate sessions on login, authorize every protected operation, fail closed, and audit security-relevant state changes without secrets.
|
||||
- Ask first: change session lifetime, cookie scope, login throttling, username normalization, credential delivery, roles, recovery, or personal-data retention.
|
||||
- Never: store plaintext passwords/raw session tokens, trust a client merchant id for authorization, put auth tokens in browser storage or URLs, reveal account existence, or commit credentials.
|
||||
|
||||
## Resolved decisions
|
||||
|
||||
- Use revocable server-side sessions rather than browser-stored JWT authentication. See ADR 0014.
|
||||
- Transport only an opaque token in a `Secure`, `HttpOnly`, `SameSite=Lax` cookie.
|
||||
- Hash passwords with Argon2id and return generic login failures.
|
||||
- Expire sessions after 12 hours idle and seven days absolute; revoke immediately on logout or account disablement.
|
||||
- Generate one demo user's credentials per merchant through a one-time administrative command, display them once, and keep them out of Git.
|
||||
|
||||
## Open decisions
|
||||
|
||||
- Exact username normalization and generated username format
|
||||
- Login rate-limit keys, thresholds, lockout behavior, and response contract
|
||||
- CSRF protection for state-changing authenticated requests
|
||||
- Number of simultaneous sessions permitted per user
|
||||
- Exact session store, token-digest algorithm, cookie name/path, and activity-write throttling
|
||||
- Credential replacement and secure delivery procedure
|
||||
- Security-event IP/user-agent collection and retention
|
||||
Reference in New Issue
Block a user