2026-07-28 02:12:46 +08:00
|
|
|
# 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
|
|
|
|
|
|
2026-07-29 23:22:40 +08:00
|
|
|
`APS_AUTH_PROVIDER=jms` enables the real JMS account/password flow. The server calls
|
2026-08-20 11:39:21 +08:00
|
|
|
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.
|
2026-07-28 02:12:46 +08:00
|
|
|
|
2026-07-29 23:22:40 +08:00
|
|
|
The JMS authentication adapter implements these operations:
|
2026-07-28 02:12:46 +08:00
|
|
|
|
|
|
|
|
```text
|
2026-07-29 23:22:40 +08:00
|
|
|
login precheck / captcha
|
2026-08-20 11:39:21 +08:00
|
|
|
login (tenant name typed by user; no public tenant discovery)
|
2026-07-29 23:22:40 +08:00
|
|
|
authenticate via profile + tenant revalidation
|
|
|
|
|
local session refresh
|
|
|
|
|
remote logout
|
2026-07-28 02:12:46 +08:00
|
|
|
```
|
|
|
|
|
|
2026-07-29 23:22:40 +08:00
|
|
|
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.
|
|
|
|
|
|
2026-07-28 02:12:46 +08:00
|
|
|
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.
|