oclaw/uds-auth/README.md
oliver 0e6de055a9
Some checks are pending
ci / test (push) Waiting to run
ci / test-postgresql (push) Waiting to run
fix(uds-auth): keep sessions until manual sign-out; refresh expired UAC tokens by QR (0.3.20)
Users were asked to sign in again every ~30 minutes, even while active.

Session lifetime
- Browser: UDS_SESSION kept its login-time Max-Age and was never re-issued,
  so the cookie vanished 30 min after login. Re-issue it on use (throttled
  by slidingInterval).
- Desktop: remote.mux WS traffic never passed the HTTP auth middleware, so
  the idle TTL never slid. assertPrincipalLive now reports activity and
  slides the session and the account's bridges (throttled).
- New setting sessionIdleTimeoutHours (Settings page + config). Default 0 =
  no automatic sign-out; only a manual sign-out ends a session, so
  conversations and night-time cron are never cut off. 0.5-168 enables an
  idle timeout with the 7-day cap. The TTL is read live.
- Stores support long TTLs: the absolute cap follows the TTL, older 7-day
  sessions are lifted on activity, and expiry timers re-arm instead of
  overflowing setTimeout (~24.8 days).

UAC token check
- Every 2 hours (and 1 min after start) each QR session's token is checked
  against icenterapi. A rejected token is only flagged, never a sign-out.
  Network errors, 5xx and bad JSON keep the previous verdict (the old 6h
  check revoked all of an account's sessions on any failure, including
  outages).
- GET /uds-auth/session-status (non-sliding) returns alive + tokenValid.
  The client probes it every minute and on focus: a gone session signs the
  UI out with a notice; a rejected token opens a refresh QR once.
- Scanning in refresh mode writes the new token into every live session of
  the account and the skill credential cache in place: no new session, no
  reload. Other accounts are rejected (token_refresh_emp_mismatch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:59:48 +08:00

6 KiB
Raw Blame History

uds-auth

0.3.10: 升级与协议变更说明 · SBOM · 第三方许可证

UDS authentication plugin for DeepSeek Harness (trusted single-instance access control — not strong multi-tenant isolation).

See package.json for the current 0.3.x version (breaking auth protocol vs 0.2.x — re-login required).

Support boundary

Item Notes
Runtime Node ^22.19.0 || >=24 (aligned with DSH Host)
Mode Single Host, trusted enterprise users, controlled skills
Out of scope Untrusted multi-tenant execution isolation; multi-instance without shared session store

Install

dsh plugin --profile web add -w "<abs>/oclaw/uds-auth"
# or clean tarball
dsh plugin add -w "<abs>/uds-auth-0.3.9.tgz"

Empty roles table requires UDS_AUTH_INITIAL_ADMIN or initialAdminEmpNo. Restart Harness after install.

ctx.get('udsAuth').runAsHost(cb) — Host background ALS.
ctx.get('udsAuth').mintTaskCapability({ empNo, dshSessionId, scopes, ttlSeconds }) — inject UDS_TASK_CAPABILITY for cron/skill.

Config

uacBaseUrl: https://uac.zte.com.cn   # HTTPS required at startup/merge
userSearchUrl: https://icenterapi.zte.com.cn/...
loginSystemCode: '100000455558'
initialAdminEmpNo: ''
allowOpenRegistration: true
allowRawAgentToken: false
outboundAllowedHosts: icenterapi.zte.com.cn,icentermsg.dt.zte.com.cn
trustedHosts: ''               # empty = loopback Host only; proxies must set allowlist
retainSkillCredentialsOnLogout: true
skillCredentialTtlSeconds: 604800
sessionIdleTimeoutHours: 0     # 0 = never auto sign out; 0.5–168h idle timeout; editable in Settings

Durable state defaults to ~/.uds-auth/ (roles / owners / credentials / bridge / sessions / config.runtime.json; override with UDS_AUTH_DATA_DIR). Security toggles take effect on the next request after Settings save. Empty outbound allowlist denies all.

Sessions

  • Lifetime: by default (sessionIdleTimeoutHours: 0) sessions never sign out on their own — only a manual sign-out ends them, so running conversations and night-time scheduled tasks are never cut off. Setting 0.5–168 hours in Settings enables an idle timeout that slides on activity (HTTP requests and Desktop WS calls) with a 7-day absolute cap. The browser cookie and the Desktop bridge token slide with the session.
  • UAC token check: every 2 hours (and 1 minute after start) each QR session's UAC token is re-checked against the icenterapi department profile. A rejected token (401/403, business error code, employee not visible/mismatched) never signs out: the client shows a QR prompt, and the scan writes the new token into every live session of that account in place — no new session, no reload. Network errors, 5xx and malformed responses keep the previous verdict.
  • Client probe: GET /uds-auth/session-status (never slides the TTL) runs every minute and on window focus. It returns alive and tokenValid; a gone session signs the UI out, a rejected token opens the refresh QR once.
  • Survives restarts: the default in-memory store is persisted to ~/.uds-auth/sessions.json and restored on Host start (emergency admins have no SSO re-login, so they depend on this).
    • Only the SHA-256 hash of each session token is stored; user data (may include the UAC token) is AES-256-GCM encrypted with the plugin key .uds-auth-secret-key.
    • Expired, undecryptable or tampered rows are dropped on load; disabled-account and emergency-password rotation checks still run on every request.
    • Delete sessions.json and restart to sign everyone out. If the key file is unavailable, sessions are simply not persisted.
  • Not used when a Redis session store is configured.

Admin / emergency

  • super_admin / admin / fallback_admin share the same admin-class permissions (identity labels, not stronger isolation).
  • The emergency account is enabled by default: username administrator, password Admin@123. Change it in Settings after first sign-in. Admins can also disable it. Older roles files without an explicit emergency-account state receive this default; an explicitly disabled account stays disabled. Historical SHA-256 hashes of Admin@123 migrate to scrypt.
  • Local unlock: node scripts/seal-local-admin.mjs (interactive / --stdin — no argv passphrase) → UDS_AUTH_LOCAL_ADMIN_BOX.
  • Removing a user disables the account by default (open registration cannot auto-revive).

DSH compatibility

Verified on DeepSeek Harness Desktop 0.2.0-rc.2. The plugin relies on DSH internals that are not a public API; after a DSH upgrade run npm test and check login, workspaces and Settings → Models on a real install:

  • Gateway prepareInvocation / resolveDescriptor / openRemoteEvents / receiveRemoteEventResult (if missing, the ACL refuses to install instead of failing open);
  • the client connection/reset event (emitted after login so boot-time caches such as the settings mirror re-read);
  • endpoint names workspace/follow, workspace/initializeDefault, and the Desktop welcome reads settings/describe, llm/listConfigurableProviders, credentials/describe, account/getState.

Desktop opens its connection anonymously at boot and reconnects after login. The plugin therefore:

  • parks anonymous stream calls (e.g. account state) without data instead of failing them (a failure is terminal for DSH and shows "sign-in failed / add API key");
  • answers anonymous workspace/follow with an empty list and anonymous workspace/initializeDefault with "nothing created";
  • holds /api/* requests for up to 15 s while the gateway ACL is still installing, instead of returning 403.

Skill / cron

Prefer Host-minted UDS_TASK_CAPABILITY + DSH_SESSION_ID with /uds-auth/outbound. Raw /agent-credentials stays off unless explicitly enabled. Python helper: skill-helpers/python/uds_skill_auth.py.

Chinese docs: README.zh.md, docs/skill-auth-standard.zh.md.

License

MIT