# Phase 1B: identity and access API All paths below start with `/api/v1`. Protected endpoints require `Authorization: Bearer `. There are no public account-creation or owner-creation endpoints. Staff account provisioning is available to authorized administrators; storefront self-registration and verified email onboarding can be added with the customer milestone. ## Endpoints | Method | Path | Permission | Request / result | | ------ | ---------------------- | ------------------ | ------------------------------------------------------------------- | | POST | /auth/login | Public; throttled | organizationId, email, password → accessToken, tokenType, expiresAt | | GET | /auth/me | Authenticated | userId, organizationId, sessionId, effective permissions | | POST | /auth/logout | Authenticated | Revoke current session; 204 | | POST | /auth/logout-all | Authenticated | Revoke all current user sessions; 204 | | POST | /auth/recovery/request | Public; throttled | organizationId, email → generic 202; 503 if SMTP is unconfigured | | POST | /auth/recovery/reset | Public; throttled | token, password → 204; token expires and can be consumed once | | GET | /users | users.read | Paginated safe user records | | POST | /users | users.create | name, email, password → PENDING account | | PATCH | /users/:id/status | users.approve | status: ACTIVE or SUSPENDED | | PATCH | /users/:id/roles | users.roles.assign | roleIds: UUID array; replaces assignments | | GET | /roles/permissions | roles.read | Current permission catalogue | | GET | /roles | roles.read | Paginated roles | | POST | /roles | roles.manage | name, permissions | | PATCH | /roles/:id | roles.manage | Complete replacement of name and permissions | | GET | /audit-events | audit.read | Paginated append-only organization audit history | List queries accept `limit` (1–100, default 25) and `offset` (0–10000, default 0). Unknown fields, malformed UUIDs, duplicate role assignments/permissions and unknown permissions are rejected. Errors use Nest's JSON error contract: 400 invalid input, 401 unauthenticated, 403 denied, 404 missing/out-of-scope target, 409 duplicate record, 429 throttled, 503 unavailable. Example login: ```json { "organizationId": "", "email": "owner@example.com", "password": "" } ``` ## Access rules Organization scope comes from the persisted session, never from an administration request body. Email uniqueness is per organization and normalized to lowercase. New accounts have no roles and cannot sign in until approved. Multiple roles combine their permissions. Permissions are reloaded on every request and rechecked inside administration transactions. Administrators cannot grant permissions they do not hold, modify a more privileged role, change their own role assignments, assign the system Owner role, or change the owner's approval state. A single installation owner is created by the CLI; API-supplied owner flags are rejected. Suspending a user revokes sessions and recovery tokens; reactivation does not restore them. An organization represents an access boundary, not a customer or supplier by default. No organization onboarding API, SaaS subscription isolation, MFA, SSO or social login is included in this milestone. ## Passwords, sessions and recovery Passwords require 15–128 characters and use salted asynchronous scrypt (N=32768, r=8, p=3). Session and recovery secrets use 256 bits of randomness; only SHA-256 token digests are persisted. Sessions expire after the configured absolute lifetime. No refresh-token flow is needed for this opaque-session design; clients sign in again after expiry. Recovery links put the token in the URL fragment; the future frontend must read it, submit it to the reset endpoint and remove it from browser history. Never log request bodies or authorization headers at the reverse proxy. Recovery resets invalidate all sessions and other recovery tokens in the same transaction. A failed SMTP send discards that token and logs a sanitized failure; users can request again after the throttle window. Recovery delivery is synchronous in this milestone. The response body does not reveal account existence, but response timing is not constant. Durable queued delivery and retries belong to the notification milestone before a public storefront launch. No real emails are sent by the test suite. ## Audit and abuse protection Successful logins, known-account login failures, logout, password recovery/reset, user provisioning/approval/suspension and role changes are audited. Mutations and audit inserts share a transaction. Audit rows accept no arbitrary metadata and database triggers reject updates/deletes. Deploy with a restricted application database role; database owners can bypass database controls. Auth routes share a database-backed IP limit of 30 requests/minute. Login also permits 10 attempts/account/15 minutes; recovery requests permit 3/account/15 minutes. Limits persist across instances. Proxy trust is disabled: behind a proxy, IP throttling groups requests under its address until an explicitly trusted proxy policy is configured. Use an edge rate limiter as well before public launch. Expired sessions, recovery tokens and rate-limit rows can be removed by a controlled housekeeping job using their indexed expiry columns. Audit retention requires an explicit archival policy and privileged maintenance procedure; the API never deletes audits. References: [Node crypto](https://nodejs.org/api/crypto.html), [Nodemailer SMTP](https://nodemailer.com/smtp).