# Authentication, tenancy, and project collaboration ## Security boundaries - Every `/api/*` route is authenticated except health and the authentication handshake. - `tenant_uuid` and `user_id` are taken only from the verified server-side identity context. - Personal sessions are authorized by `(tenant_uuid, owner_user_id)`. - Project sessions, files, and scheduling state are authorized through `aps_project_members`. - Tenant-scoped world, checkpoint, knowledge, embedding, preference, and report files use namespaced paths. - Cross-boundary resource lookups return `404` where practical so callers cannot enumerate resources. - Web credentials and desktop-license credentials use separate HttpOnly cookies and cannot substitute for each other. ## Authentication providers `APS_AUTH_PROVIDER=jms` enables the real JMS account/password flow. The server calls login precheck, captcha, login, profile, tenant, and logout endpoints at `JMS_AUTH_BASE_URL`. The browser submits a hand-typed enterprise **name** (no public tenant list — that API was removed to avoid leaking tenant directory). The server maps known aliases (e.g. `平台` → `platform`), forwards name/code to JMS login, then revalidates the authenticated tenant from JMS before binding identity. Optional `JMS_AUTH_TENANT_CODE` / `JMS_AUTH_TENANT_NAME` can lock the deployment to one tenant. `unconfigured` keeps every business API closed. The JMS authentication adapter implements these operations: ```text login precheck / captcha login (tenant name typed by user; no public tenant discovery) authenticate via profile + tenant revalidation local session refresh remote logout ``` User-directory search remains closed with `AUTH_USER_DIRECTORY_NOT_CONFIGURED` until the user-management scope is integrated. The current JMS OpenAPI has no self-registration or unauthenticated self-service password-recovery endpoint. Account creation and password reset remain enterprise-admin workflows. The normalized identity is: ```text user_id, username, fullname, tenant_uuid, roles, expires_at ``` The external adapter must reject deleted or disabled users and tenants. Client-supplied tenant or user identifiers are never used as authorization input. ## Desktop license provider Electron clients do not show account or phone login. A stable installation UUID is stored in `~/.aps/device-id`; an authorization code is activated through `POST /api/auth/license/activate` and bound to that installation. The server records only hashes of the code and device identifier. `APS_LICENSE_PROVIDER=unconfigured` is the production-safe default. `APS_LICENSE_PROVIDER=mock` is development-only and includes hour, day, week, month, year, and permanent demo grants. Demo code strings are reusable across developer installations, but every resulting activation remains device-bound. License duration is determined by the validated code, never by a client-side selection. Every desktop business request revalidates the activation row, device binding, status, revocation state, and expiry. The temporary desktop identity derives a stable `user_id` from the installation UUID so personal projects and sessions retain `(tenant_uuid, owner_user_id)` isolation. The real license service adapter must return the authoritative tenant and licensed seat identity. The external license integration must implement: ```text activate / validate_code authenticate / validate_activation refresh logout revocation and expiry lookup ``` ## Project roles | Action | owner | editor | viewer | | --- | --- | --- | --- | | Read project, chat, files, schedule | yes | yes | yes | | Edit data and run scheduling | yes | yes | no | | Add project sessions and files | yes | yes | no | | Manage members and delete project | yes | no | no | A project starts with one owner membership. Sharing adds a membership in the same tenant; it never copies project data. Personal sessions remain private even when the same user also belongs to shared projects. ## Storage model - `aps_workspace_projects`: project owner and collaborative data version. - `aps_project_members`: tenant-local project ACL. - `aps_user_workspaces`: per-user active project and session. - `aps_chat_sessions` and `aps_chat_messages`: personal/project scoped conversations. - `aps_project_files`: project asset metadata. - `aps_schedule_runs` and `aps_schedule_results`: scheduling execution/version records. - `aps_audit_events`: actor, tenant, project, action, and resource audit trail. - `aps_license_activations`: hashed authorization-code binding, device owner, duration, expiry, and revocation state. Existing master-data tables include `tenant_uuid`; platform routing templates use the explicit `platform` tenant and are read-only to normal tenants. ## Database rollout New databases use Alembic revisions `20260727_01` and `20260728_02`. Existing SQLite development databases receive compatibility columns at startup. Production MySQL upgrades must be tested on a copy, and existing rows must be mapped to an explicit tenant and owner before constraints are made authoritative. Recommended cutover: 1. Back up the database and JSON storage. 2. Map every historical project to a tenant and owner. 3. Run `alembic upgrade head` for a new APS schema, or apply a reviewed delta migration. 4. Import project, session, message, file, and world data. 5. Verify row counts, tenant assignments, and project memberships. 6. Enable the external auth provider and run the isolation test suite. 7. Keep old JSON data read-only during the rollback window. ## External API mapping checklist - Login request and response fields. - Token verification or introspection endpoint. - Cookie/Bearer transport, expiry, refresh, and revocation behavior. - User and tenant state fields and deletion semantics. - Tenant-local user search endpoint. - Role/permission claims and MFA or forced-password-change responses. - License-code validation response, duration units, tenant/seat mapping, device limits, revocation, and offline grace policy.