Skip to content

[capability-policy] REST-created local users (user/role admin surface + dynamic auth) #5272

Description

@zetyquickly

Part of #5261 (epic). Prerequisite for the manual DB-wired test — without it you cannot act as director@ vs Bob vs Carol vs engineering@ on localhost, so none of the per-user capability flow is testable by hand.

Redesign (supersedes the env-token approach)

The original cut was an env-var token table (IRONCLAW_REBORN_USER_TOKENSStaticUserTokenAuthenticator, PR #5286). That is superseded. Users are created through REST — the same admin surface the future UI will drive — not pre-listed in an env var. IRONCLAW_REBORN_USER_TOKENS and StaticUserTokenAuthenticator are removed.

This issue owns identity (users / roles / auth); #5268 owns capability policy (what those users can do). The two are independent admin surfaces on the same gateway.

What

  1. Durable user storeuser_id → { role, token_hash } plus a token_hash → user_id index, persisted over the durable /tenants mount (survives restart, like the feat(reborn): scoped-lifecycle admin install store (#3288) — availability foundation for #5261 #4544 grants). Tokens stored hashed, never plaintext.

  2. Admin REST surface (admin-gated by WebUiAuthenticatedCaller::is_admin(), the [capability-policy] Reborn DB-backed user role + admin gate (UserRole Owner>Admin>Member) #5266 role) — the durable contract the UI will reuse:

    • POST /api/webchat/v2/admin/users { user_id, role } → create the user; returns { user_id, role, token } (the token is a local-dev affordance so you can "be" that user without SSO).
    • PUT /api/webchat/v2/admin/users/{user_id}/role { role } → set/change role (the UI's "role setting").
    • GET /api/webchat/v2/admin/users → list. DELETE /api/webchat/v2/admin/users/{user_id} → remove.
  3. Dynamic authenticator (ironclaw_reborn_webui_ingress) — resolves a bearer against the user store → WebuiAuthentication{ user_id, role }, layered over the operator EnvBearerAuthenticator (which stays as the bootstrap admin that creates the first users + keeps the SSO signing key / runtime-owner pin). Does not grant operator_webui_config; admin-ness travels via the role.

UI-reusability (by design)

The endpoints live on the canonical WebChat-v2 gateway behind the standard descriptor-driven body/rate-limit + bearer middleware, and the handlers consume an authenticated caller{user, role} agnostic to how auth happened. So the future UI (user-creation + role-setting screens) issues these exact calls; only the authenticator differs (local-dev token vs production session/SSO). Token-minting is decoupled from the user/role semantics so the SSO path reuses create-user + set-role unchanged.

Flow

operator (env-bearer, bootstrap) → POST /admin/users creates director@(Admin) / Bob / Carol / engineering@ → each gets a token → they act with it; director@ grants capabilities via #5268. All durable across restart.

Scope guardrails

  • Local-dev / standalone only. Production multi-user uses SessionAuthenticator / OidcAuthenticator; this issue does not change that.
  • The turn-runner already isolates each caller's threads per-turn via ThreadScopeResolver::resolve_for_turn, so multiple users through one process are supported at the scope layer.

Depends on: #5266 (the UserRole on the caller).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions