# Authentication And Authorization / 认证与鉴权 ## 中文 ### 当前认证模型 `accounts.svc.plus` 不是“纯 JWT API”,而是: - 以 session token 为主控制面事实来源。 - 以 `xc_session` cookie 和 `Authorization: Bearer ` 为主调用方式。 - 在 `auth.enable` 打开时,为部分路由叠加 JWT middleware。 - 对 `/api/internal/*` 使用 internal service token。 - 对 `/api/agent-server/v1/users|status` 使用 agent token。 这意味着“JWT 已启用”并不等于“业务只靠 JWT 运行”;很多 handler 仍会继续读取 session。 ### 认证方式总表 | 方式 | 典型接口 | 传入位置 | 成功后得到什么 | | --- | --- | --- | --- | | Session token | `/api/auth/login` `/api/auth/session` `/api/auth/xworkmate/*` | `Authorization` 或 `xc_session` cookie | 当前用户上下文、管理员权限、XWorkmate profile 读写能力。 | | JWT refresh | `POST /api/auth/token/refresh` | JSON body `refresh_token` | 新 `access_token`。 | | OAuth exchange code | `POST /api/auth/token/exchange` | JSON body `exchange_code` | 真实 session token,字段名同时以 `token` / `access_token` 返回。 | | Internal service token | `/api/internal/*` | `Authorization: Bearer ` | 受信任服务读接口。 | | Agent token | `/api/agent-server/v1/users` `/api/agent-server/v1/status` | `Authorization: Bearer ` | Agent 身份、client 列表拉取、状态上报。 | ### Session issuance paths 以下路径会签发新的 session token: | 入口 | 请求字段 | 成功返回 | 备注 | | --- | --- | --- | --- | | `POST /api/auth/login` | `identifier/account/username/email` + `password`,或邮箱 + `totpCode` | `message`、`token`、`access_token`、`expiresAt`、`expires_in`、`user` | 如果用户已启用 MFA 且未提交验证码,则不会发 session,而是先返回 MFA challenge。 | | `POST /api/auth/register/verify` | `email`、`code` | `message`、`token`、`expiresAt`、`user` 或 `verified=true` | 已创建用户走“验证邮箱并自动登录”;预注册验证码走“仅标记 verified”。 | | `POST /api/auth/password/reset/confirm` | `token`、`password` | `message`、`token`、`expiresAt`、`user` | 重置密码成功后直接进入新会话。 | | `POST /api/auth/mfa/verify` | `mfa_ticket/mfaToken`、`code/totpCode` | `message`、`token`、`access_token`、`expiresAt`、`expires_in`、`user` | 登录后补做 MFA 的完成步骤。 | | `POST /api/auth/mfa/totp/verify` | `token`、`code` | `message`、`token`、`expiresAt`、`user` | 首次启用 TOTP 成功后直接签发 session。 | | `GET /api/auth/oauth/callback/:provider` | query `code` | `307` redirect 到前端 `/login?exchange_code=...` | callback 自身不直接输出 JSON,而是先创建 session,再下发一次性 exchange code。 | ### 注册与邮箱验证 #### `POST /api/auth/register/send` | 项 | 内容 | | --- | --- | | 请求字段 | `email` | | 成功返回 | `200 {"message":"verification email sent"}` | | 前置条件 | 邮箱格式合法;不在 blacklist;若邮箱已存在且未验证,则继续发送验证邮件。 | | 失败返回 | `invalid_request`、`invalid_email`、`email_blacklisted`、`smtp_timeout`、`verification_failed`、`email_already_exists`。 | #### `POST /api/auth/register` | 项 | 内容 | | --- | --- | | 请求字段 | `name`、`email`、`password`、`code` | | 成功返回 | `201 {"message":"registration successful","user":...}` | | 前置条件 | `name` 非空;邮箱合法;密码长度至少 8;若启用邮箱验证则 `code` 必须存在且匹配。 | | 关键副作用 | 创建 `store.User`;自动 upsert 7 天 trial `store.Subscription`。 | | 失败返回 | `name_required`、`missing_credentials`、`invalid_email`、`password_too_short`、`verification_required`、`invalid_code`、`email_already_exists`、`name_already_exists`。 | #### `POST /api/auth/register/verify` | 项 | 内容 | | --- | --- | | 请求字段 | `email`、`code` | | 成功返回 A | `200 {"message":"email verified","token":"...","expiresAt":"...","user":...}` | | 成功返回 B | `200 {"message":"verification successful","verified":true}` | | 分支解释 | 若命中已创建用户的 email verification,接口会把用户标记为已验证并直接签发 session;若命中注册前验证码,只做“验证码通过”标记。 | | 失败返回 | `token_in_query`、`invalid_request`、`invalid_code`、`verification_failed`、`session_creation_failed`。 | ### 登录、MFA challenge 与会话读取 #### `POST /api/auth/login` | 项 | 内容 | | --- | --- | | 请求字段 | `identifier`、`account`、`username`、`email`、`password`、`totpCode` | | 标准成功返回 | `message`、`token`、`access_token`、`expiresAt`、`expires_in`、`mfaRequired=false`、`user` | | MFA challenge 返回 | `message="mfa required"`、`mfaRequired=true`、`mfaMethod="totp"`、`mfaTicket`、`mfa_ticket`、兼容字段 `mfaToken` | | 真实规则 | 先按 `identifier -> account -> username -> email` 解析登录标识。若用户已启用 MFA 但未提交 `totpCode`,登录不发 session,只发 challenge。 | | 失败返回 | `credentials_in_query`、`invalid_request`、`missing_credentials`、`user_not_found`、`invalid_credentials`、`password_required`、`email_not_verified`、`sandbox_no_login`、`mfa_challenge_creation_failed`。 | #### `POST /api/auth/mfa/verify` | 项 | 内容 | | --- | --- | | 请求字段 | `mfa_ticket`、`mfaToken`、`code`、`totpCode`、`method` | | 成功返回 | `message`、`token`、`access_token`、`expiresAt`、`expires_in`、`user` | | 前置条件 | `method` 仅支持 `totp`;ticket 必须存在且未过期;用户已启用 MFA。 | | 失败返回 | `mfa_ticket_required`、`mfa_code_required`、`unsupported_mfa_method`、`invalid_mfa_ticket`、`mfa_not_enabled`、`invalid_mfa_code`。 | #### `GET /api/auth/session` | 项 | 内容 | | --- | --- | | 认证 | 需要有效 session;若启用 token middleware,还会先经过 JWT + active-user 检查。 | | 成功返回 | `{"user": ...}`,其中可能附带 `tenantId`、`tenants`、XWorkmate access 视图。 | | 失败返回 | `session_token_required`、`invalid_session`、`session_user_lookup_failed`、`account_suspended`。 | #### `DELETE /api/auth/session` | 项 | 内容 | | --- | --- | | 认证 | 需要有效 session。 | | 成功返回 | `204 No Content` | | 副作用 | 删除进程内 session,并清空 cookie。 | ### TOTP 启用、查询与关闭 #### `POST /api/auth/mfa/totp/provision` | 项 | 内容 | | --- | --- | | 请求字段 | `token`、`issuer`、`account` | | 成功返回 | `secret`、`otpauth_url`、`issuer`、`account`、`mfaToken`、`mfa`、`user` | | 入口模式 | 可使用已有 `mfa token`,也可使用当前 session 自动创建新的 MFA challenge。 | | 失败返回 | `invalid_request`、`invalid_mfa_token`、`mfa_token_required`、`invalid_session`、`mfa_already_enabled`、`read_only_account`、`mfa_secret_generation_failed`、`mfa_setup_failed`。 | #### `POST /api/auth/mfa/totp/verify` | 项 | 内容 | | --- | --- | | 请求字段 | `token`、`code` | | 成功返回 | `{"message":"mfa_verified","token":"...","expiresAt":"...","user":...}` | | 特殊失败 | 多次错误会进入 `429 {"error":"mfa_challenge_locked","retryAt":"...","mfaToken":"..."}`。 | | 常见失败 | `mfa_token_required`、`invalid_mfa_token`、`mfa_secret_missing`、`mfa_code_required`、`invalid_mfa_code`、`mfa_update_failed`。 | #### `GET /api/auth/mfa/status` | 项 | 内容 | | --- | --- | | 输入来源 | query `token` / `identifier` / `email`,header `X-MFA-Token`,或 `Authorization` session token。 | | 成功返回 | `enabled`、`mfa`、`user`。若按 identifier 查询且用户不存在,返回 `200 {"mfa_enabled":false}`。 | | 失败返回 | `mfa_status_failed`、`mfa_token_required`。 | #### `POST /api/auth/mfa/disable` | 项 | 内容 | | --- | --- | | 认证 | 当前 session token,优先从 `Authorization` 读取,也接受 query `token`。 | | 成功返回 | `{"message":"mfa_disabled","user":...}` | | 失败返回 | `session_token_required`、`invalid_session`、`mfa_disable_failed`、`mfa_not_enabled`、`read_only_account`。 | ### OAuth 与 exchange code #### `GET /api/auth/oauth/login/:provider` | 项 | 内容 | | --- | --- | | 路径参数 | `provider`,当前实现支持 `github` 与 `google`。 | | 成功返回 | `307 Temporary Redirect` 到 provider authorization URL。 | | 失败返回 | `provider_not_found`。 | #### `GET /api/auth/oauth/callback/:provider` | 项 | 内容 | | --- | --- | | 输入 | query `code`,可选由 `state` 带回前端 URL。 | | 成功行为 | 交换 provider token,拉取 profile,创建或复用用户,确保 `store.Identity` 绑定,然后签发 session + 一次性 exchange code,最后 `307` 跳转到前端 `/login?exchange_code=...`。 | | 失败返回 | `provider_not_found`、`code_missing`、`oauth_exchange_failed`、`fetch_profile_failed`、`email_missing`、`email_not_verified`、`store_error`、`user_creation_failed`、`session_creation_failed`、`exchange_code_creation_failed`。 | #### `POST /api/auth/token/exchange` | 项 | 内容 | | --- | --- | | 请求字段 | `exchange_code` | | 成功返回 | `token`、`access_token`、`token_type`、`expiresAt`、`expires_in`、`user` | | 语义 | 不是按 user claims 自签 token,而是把 callback 阶段暂存的一次性 code 换回真实 session。 | | 失败返回 | `invalid_request`、`invalid_exchange_code`、`session_user_lookup_failed`。 | ### JWT refresh #### `POST /api/auth/token/refresh` | 项 | 内容 | | --- | --- | | 请求字段 | `refresh_token` | | 成功返回 | `access_token`、`token_type="Bearer"`、`expires_in` | | 前置条件 | `h.tokenService != nil`;refresh token 合法且未过期。 | | 失败返回 | `token_service_unavailable`、`invalid_request`、`invalid_refresh_token`。 | #### `POST /api/auth/refresh` 别名路由,行为与 `POST /api/auth/token/refresh` 完全一致。 ### 密码重置 #### `POST /api/auth/password/reset` | 项 | 内容 | | --- | --- | | 请求字段 | `email` | | 成功返回 | `202 {"message":"if the account exists a reset email will be sent"}` | | 实现说明 | handler 本身按邮箱驱动;但该路由被挂在 authProtected 组下,因此启用 JWT middleware 后会多一层前置认证。 | | 失败返回 | `email_in_query`、`invalid_request`、`email_required`、`password_reset_failed`、`read_only_account`。 | #### `POST /api/auth/password/reset/confirm` | 项 | 内容 | | --- | --- | | 请求字段 | `token`、`password` | | 成功返回 | `message`、`token`、`expiresAt`、`user` | | 前置条件 | password 长度至少 8;reset token 有效;若用户是 demo/read-only account 则拒绝。 | | 失败返回 | `credentials_in_query`、`invalid_request`、`password_too_short`、`invalid_token`、`password_reset_failed`、`read_only_account`、`session_creation_failed`。 | ### XWorkmate profile 与 Vault-backed secrets #### `GET /api/auth/xworkmate/profile` | 项 | 内容 | | --- | --- | | 成功返回 | `edition`、`tenant`、`membershipRole`、`profileScope`、`canEditIntegrations`、`canManageTenant`、`profile`、`tokenConfigured` | | profile 字段 | `BRIDGE_SERVER_URL`、`bridgeServerOrigin`、`vaultUrl`、`vaultNamespace`、`vaultSecretPath`、`vaultSecretKey`、`secretLocators`、`apisixUrl` | | 失败返回 | `tenant_membership_required`、`tenant_not_found`、`xworkmate_context_failed`、`xworkmate_profile_read_failed`。 | #### `GET /api/auth/xworkmate/profile/sync` | 项 | 内容 | | --- | --- | | 成功返回 | `BRIDGE_SERVER_URL`、`BRIDGE_AUTH_TOKEN` | | 语义 | 给 bridge / desktop sync 使用的精简同步视图,不返回完整 profile。 | | 失败返回 | `tenant_membership_required`、`tenant_not_found`、`bridge_server_url_unavailable`、`bridge_auth_token_unavailable`。 | #### `PUT /api/auth/xworkmate/profile` | 项 | 内容 | | --- | --- | | 请求字段 | 允许直接传 profile payload,或 `{ "profile": {...} }` | | 禁止字段 | raw token/password/api key 不允许持久化;命中时返回 `token_persistence_forbidden`。 | | 成功返回 | 与 `GET /xworkmate/profile` 同形。 | | 失败返回 | `xworkmate_profile_forbidden`、`read_only_account`、`invalid_request`、`token_persistence_forbidden`、`xworkmate_profile_write_failed`。 | #### `GET /api/auth/xworkmate/secrets` | 项 | 内容 | | --- | --- | | 成功返回 | `tenant`、`membershipRole`、`profileScope`、`canEditIntegrations`、`vaultBackendEnabled`、`tokenConfigured`、`secrets` | | `secrets[]` 元素 | `target`、`provider`、`secretPath`、`secretKey`、`configured`、`required` 等元数据;绝不返回 raw secret。 | | 失败返回 | `xworkmate_secret_read_failed`、`xworkmate_context_failed`、`tenant_not_found`。 | #### `PUT /api/auth/xworkmate/secrets/:target` | 项 | 内容 | | --- | --- | | 路径参数 | `target`,例如 bridge auth token 对应的 secret target。 | | 请求字段 | `value` | | 成功返回 | `secret`、`profileScope`、`tokenConfigured` | | 失败返回 | `xworkmate_secret_forbidden`、`read_only_account`、`xworkmate_secret_unknown_target`、`xworkmate_secret_value_required`、`xworkmate_secret_write_failed`、`xworkmate_profile_write_failed`。 | #### `DELETE /api/auth/xworkmate/secrets/:target` | 项 | 内容 | | --- | --- | | 路径参数 | `target` | | 成功返回 | `secret`、`profileScope`、`tokenConfigured` | | 语义 | 删除 Vault/backend secret,但保留 locator 元数据。 | | 失败返回 | `xworkmate_secret_forbidden`、`read_only_account`、`xworkmate_secret_unknown_target`、`xworkmate_secret_delete_failed`。 | ## English ### Current Auth Model `accounts.svc.plus` is not a pure JWT API. It is: - session-first for the current control plane, - primarily called through the `xc_session` cookie or `Authorization: Bearer `, - optionally wrapped by JWT middleware when `auth.enable` is on, - protected by an internal service token for `/api/internal/*`, - and protected by agent tokens for `/api/agent-server/v1/users|status`. So “JWT enabled” does not mean “business logic runs only on JWT”; many handlers still resolve sessions explicitly. ### Authentication Modes | Mode | Example APIs | Input location | Successful outcome | | --- | --- | --- | --- | | Session token | `/api/auth/login`, `/api/auth/session`, `/api/auth/xworkmate/*` | `Authorization` header or `xc_session` cookie | User context, admin permissions, XWorkmate profile access. | | JWT refresh | `POST /api/auth/token/refresh` | JSON body `refresh_token` | A new `access_token`. | | OAuth exchange code | `POST /api/auth/token/exchange` | JSON body `exchange_code` | The real session token, returned in both `token` and `access_token`. | | Internal service token | `/api/internal/*` | `Authorization: Bearer ` | Trusted service-to-service reads. | | Agent token | `/api/agent-server/v1/users`, `/api/agent-server/v1/status` | `Authorization: Bearer ` | Agent identity, client-list reads, status reporting. | ### Session Issuance Paths The following flows mint a new session token: | Entry point | Request fields | Success shape | Notes | | --- | --- | --- | --- | | `POST /api/auth/login` | `identifier/account/username/email` plus `password`, or email plus `totpCode` | `message`, `token`, `access_token`, `expiresAt`, `expires_in`, `user` | If MFA is enabled and no TOTP code is provided, the handler returns an MFA challenge instead of a session. | | `POST /api/auth/register/verify` | `email`, `code` | `message`, `token`, `expiresAt`, `user` or `verified=true` | Existing-user email verification auto-logs the user in; pre-registration verification only marks the email as verified. | | `POST /api/auth/password/reset/confirm` | `token`, `password` | `message`, `token`, `expiresAt`, `user` | A successful password reset immediately creates a session. | | `POST /api/auth/mfa/verify` | `mfa_ticket/mfaToken`, `code/totpCode` | `message`, `token`, `access_token`, `expiresAt`, `expires_in`, `user` | Completes the MFA step after login. | | `POST /api/auth/mfa/totp/verify` | `token`, `code` | `message`, `token`, `expiresAt`, `user` | First-time TOTP enablement also ends with a new session. | | `GET /api/auth/oauth/callback/:provider` | query `code` | `307` redirect to frontend `/login?exchange_code=...` | The callback creates the session first, then issues a one-time exchange code instead of returning JSON directly. | ### Registration And Email Verification #### `POST /api/auth/register/send` | Item | Details | | --- | --- | | Request fields | `email` | | Success | `200 {"message":"verification email sent"}` | | Preconditions | Valid email format; not blacklisted; existing unverified users can still receive verification mail. | | Failures | `invalid_request`, `invalid_email`, `email_blacklisted`, `smtp_timeout`, `verification_failed`, `email_already_exists`. | #### `POST /api/auth/register` | Item | Details | | --- | --- | | Request fields | `name`, `email`, `password`, `code` | | Success | `201 {"message":"registration successful","user":...}` | | Preconditions | Non-empty `name`; valid email; password length at least 8; when email verification is enabled, `code` must exist and match. | | Side effects | Creates `store.User` and upserts a 7-day trial `store.Subscription`. | | Failures | `name_required`, `missing_credentials`, `invalid_email`, `password_too_short`, `verification_required`, `invalid_code`, `email_already_exists`, `name_already_exists`. | #### `POST /api/auth/register/verify` | Item | Details | | --- | --- | | Request fields | `email`, `code` | | Success A | `200 {"message":"email verified","token":"...","expiresAt":"...","user":...}` | | Success B | `200 {"message":"verification successful","verified":true}` | | Branching | Existing-user verification marks the user as verified and issues a session; pre-registration verification only confirms the code. | | Failures | `token_in_query`, `invalid_request`, `invalid_code`, `verification_failed`, `session_creation_failed`. | ### Login, MFA Challenge, And Session Reads #### `POST /api/auth/login` | Item | Details | | --- | --- | | Request fields | `identifier`, `account`, `username`, `email`, `password`, `totpCode` | | Standard success | `message`, `token`, `access_token`, `expiresAt`, `expires_in`, `mfaRequired=false`, `user` | | MFA challenge | `message="mfa required"`, `mfaRequired=true`, `mfaMethod="totp"`, `mfaTicket`, `mfa_ticket`, compatibility field `mfaToken` | | Behavior | Identifier resolution order is `identifier -> account -> username -> email`. If MFA is enabled and no TOTP code is provided, the handler returns a challenge instead of a session. | | Failures | `credentials_in_query`, `invalid_request`, `missing_credentials`, `user_not_found`, `invalid_credentials`, `password_required`, `email_not_verified`, `sandbox_no_login`, `mfa_challenge_creation_failed`. | #### `POST /api/auth/mfa/verify` | Item | Details | | --- | --- | | Request fields | `mfa_ticket`, `mfaToken`, `code`, `totpCode`, `method` | | Success | `message`, `token`, `access_token`, `expiresAt`, `expires_in`, `user` | | Preconditions | Only `totp` is supported; the ticket must exist and not be expired; the user must have MFA enabled. | | Failures | `mfa_ticket_required`, `mfa_code_required`, `unsupported_mfa_method`, `invalid_mfa_ticket`, `mfa_not_enabled`, `invalid_mfa_code`. | #### `GET /api/auth/session` | Item | Details | | --- | --- | | Auth | Requires a valid session. If token middleware is enabled, the request also passes JWT + active-user checks first. | | Success | `{"user": ...}` and may include `tenantId`, `tenants`, and XWorkmate access metadata. | | Failures | `session_token_required`, `invalid_session`, `session_user_lookup_failed`, `account_suspended`. | #### `DELETE /api/auth/session` | Item | Details | | --- | --- | | Auth | Requires a valid session. | | Success | `204 No Content` | | Side effect | Removes the process-local session and clears the cookie. | ### TOTP Provisioning, Status, And Disable #### `POST /api/auth/mfa/totp/provision` | Item | Details | | --- | --- | | Request fields | `token`, `issuer`, `account` | | Success | `secret`, `otpauth_url`, `issuer`, `account`, `mfaToken`, `mfa`, `user` | | Entry modes | Uses an existing MFA token or can bootstrap a new MFA challenge from the current session. | | Failures | `invalid_request`, `invalid_mfa_token`, `mfa_token_required`, `invalid_session`, `mfa_already_enabled`, `read_only_account`, `mfa_secret_generation_failed`, `mfa_setup_failed`. | #### `POST /api/auth/mfa/totp/verify` | Item | Details | | --- | --- | | Request fields | `token`, `code` | | Success | `{"message":"mfa_verified","token":"...","expiresAt":"...","user":...}` | | Special failure | Repeated failures can lock the challenge and return `429 {"error":"mfa_challenge_locked","retryAt":"...","mfaToken":"..."}`. | | Common failures | `mfa_token_required`, `invalid_mfa_token`, `mfa_secret_missing`, `mfa_code_required`, `invalid_mfa_code`, `mfa_update_failed`. | #### `GET /api/auth/mfa/status` | Item | Details | | --- | --- | | Inputs | query `token` / `identifier` / `email`, header `X-MFA-Token`, or `Authorization` session token. | | Success | `enabled`, `mfa`, `user`. If queried by identifier and the user does not exist, it returns `200 {"mfa_enabled":false}`. | | Failures | `mfa_status_failed`, `mfa_token_required`. | #### `POST /api/auth/mfa/disable` | Item | Details | | --- | --- | | Auth | Current session token, preferably from `Authorization`; also accepts query `token`. | | Success | `{"message":"mfa_disabled","user":...}` | | Failures | `session_token_required`, `invalid_session`, `mfa_disable_failed`, `mfa_not_enabled`, `read_only_account`. | ### OAuth And Exchange Code #### `GET /api/auth/oauth/login/:provider` | Item | Details | | --- | --- | | Path param | `provider`; the current code supports `github` and `google`. | | Success | `307 Temporary Redirect` to the provider authorization URL. | | Failure | `provider_not_found`. | #### `GET /api/auth/oauth/callback/:provider` | Item | Details | | --- | --- | | Input | query `code`; the `state` value may carry a frontend URL. | | Success behavior | Exchanges the provider code, fetches the profile, creates or reuses the user, ensures the `store.Identity` binding exists, issues a session plus a one-time exchange code, then redirects to the frontend `/login?exchange_code=...`. | | Failures | `provider_not_found`, `code_missing`, `oauth_exchange_failed`, `fetch_profile_failed`, `email_missing`, `email_not_verified`, `store_error`, `user_creation_failed`, `session_creation_failed`, `exchange_code_creation_failed`. | #### `POST /api/auth/token/exchange` | Item | Details | | --- | --- | | Request fields | `exchange_code` | | Success | `token`, `access_token`, `token_type`, `expiresAt`, `expires_in`, `user` | | Semantics | This does not mint a token from caller-provided claims. It converts the one-time code created during the OAuth callback back into the real session. | | Failures | `invalid_request`, `invalid_exchange_code`, `session_user_lookup_failed`. | ### JWT Refresh #### `POST /api/auth/token/refresh` | Item | Details | | --- | --- | | Request fields | `refresh_token` | | Success | `access_token`, `token_type="Bearer"`, `expires_in` | | Preconditions | `h.tokenService != nil`; the refresh token must be valid and not expired. | | Failures | `token_service_unavailable`, `invalid_request`, `invalid_refresh_token`. | #### `POST /api/auth/refresh` Alias route with the exact same behavior as `POST /api/auth/token/refresh`. ### Password Reset #### `POST /api/auth/password/reset` | Item | Details | | --- | --- | | Request fields | `email` | | Success | `202 {"message":"if the account exists a reset email will be sent"}` | | Implementation note | The handler itself is email-driven, but the route is mounted under the protected auth group, so enabling JWT middleware adds an extra precondition. | | Failures | `email_in_query`, `invalid_request`, `email_required`, `password_reset_failed`, `read_only_account`. | #### `POST /api/auth/password/reset/confirm` | Item | Details | | --- | --- | | Request fields | `token`, `password` | | Success | `message`, `token`, `expiresAt`, `user` | | Preconditions | Password length at least 8; reset token must be valid; demo/read-only users are rejected. | | Failures | `credentials_in_query`, `invalid_request`, `password_too_short`, `invalid_token`, `password_reset_failed`, `read_only_account`, `session_creation_failed`. | ### XWorkmate Profile And Vault-Backed Secrets #### `GET /api/auth/xworkmate/profile` | Item | Details | | --- | --- | | Success | `edition`, `tenant`, `membershipRole`, `profileScope`, `canEditIntegrations`, `canManageTenant`, `profile`, `tokenConfigured` | | `profile` fields | `BRIDGE_SERVER_URL`, `bridgeServerOrigin`, `vaultUrl`, `vaultNamespace`, `vaultSecretPath`, `vaultSecretKey`, `secretLocators`, `apisixUrl` | | Failures | `tenant_membership_required`, `tenant_not_found`, `xworkmate_context_failed`, `xworkmate_profile_read_failed`. | #### `GET /api/auth/xworkmate/profile/sync` | Item | Details | | --- | --- | | Success | `BRIDGE_SERVER_URL`, `BRIDGE_AUTH_TOKEN` | | Semantics | A reduced sync view used by bridge / desktop flows instead of the full profile. | | Failures | `tenant_membership_required`, `tenant_not_found`, `bridge_server_url_unavailable`, `bridge_auth_token_unavailable`. | #### `PUT /api/auth/xworkmate/profile` | Item | Details | | --- | --- | | Request fields | Accepts either a raw profile payload or `{ "profile": {...} }` | | Forbidden fields | Raw token/password/api-key persistence is rejected with `token_persistence_forbidden`. | | Success | Same shape as `GET /xworkmate/profile`. | | Failures | `xworkmate_profile_forbidden`, `read_only_account`, `invalid_request`, `token_persistence_forbidden`, `xworkmate_profile_write_failed`. | #### `GET /api/auth/xworkmate/secrets` | Item | Details | | --- | --- | | Success | `tenant`, `membershipRole`, `profileScope`, `canEditIntegrations`, `vaultBackendEnabled`, `tokenConfigured`, `secrets` | | `secrets[]` | Contains `target`, `provider`, `secretPath`, `secretKey`, `configured`, `required`, and similar metadata. It never returns the raw secret value. | | Failures | `xworkmate_secret_read_failed`, `xworkmate_context_failed`, `tenant_not_found`. | #### `PUT /api/auth/xworkmate/secrets/:target` | Item | Details | | --- | --- | | Path param | `target`, for example the bridge-auth-token secret target. | | Request fields | `value` | | Success | `secret`, `profileScope`, `tokenConfigured` | | Failures | `xworkmate_secret_forbidden`, `read_only_account`, `xworkmate_secret_unknown_target`, `xworkmate_secret_value_required`, `xworkmate_secret_write_failed`, `xworkmate_profile_write_failed`. | #### `DELETE /api/auth/xworkmate/secrets/:target` | Item | Details | | --- | --- | | Path param | `target` | | Success | `secret`, `profileScope`, `tokenConfigured` | | Semantics | Deletes the Vault/backend secret while preserving locator metadata. | | Failures | `xworkmate_secret_forbidden`, `read_only_account`, `xworkmate_secret_unknown_target`, `xworkmate_secret_delete_failed`. |