Skip to content

Auth: one auth key with RBAC, reach and ownership in storage - #75

Merged
Iamsdt merged 10 commits into
mainfrom
feat/auth-rbac
Nov 8, 2026
Merged

Iamsdt merged 10 commits into
mainfrom
feat/auth-rbac

Conversation

@Iamsdt

@Iamsdt Iamsdt commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator

Part of the 1.0.0 auth revision. Replaces the authorization key and AuthorizationBackend with
one auth key that carries the role config, builds a core Identity per request, and lets storage
enforce ownership by the caller's reach. Depends on core 10xgraph branch feat/auth-identity
(Identity, Reach, ThreadNotFoundError); merge core first.

What changes

  • One auth key. method (none | jwt | custom + path) plus RBAC keys next to it:
    roles (each with permissions, inherits, required reach), roles_claim,
    permissions_claim, tenant_claims, default_role, system_identities. "auth": "jwt" still
    works: default permission set, reach own.
  • One validator, check_auth_config, for boot, 10xgraph config, audit and build. It refuses
    a leftover authorization or graph_introspection key, unknown roles, permissions or reach,
    inheritance cycles, reach tenant without tenant_claims, and both roles_claim and
    permissions_claim. tenant_claims must match the tenant columns on the checkpointer and store
    (refused in production, a warning otherwise).
  • Permission registry (core/auth/permissions_registry.py): graph (read, invoke, stream, stop,
    fix), threads (read, write, delete), store, files (read, upload, delete), config:read,
    tasks (trigger, read, cancel), admin (read, write); wildcards resource:* and *. Default set:
    everything except admin:*. Token scope / scopes only narrow.
  • BaseAuth.authorize(identity, permission, resource=None), async, defaults to the RBAC check.
    It takes no request, so the jobs worker calls it too. Every transport passes the thread or file id
    as resource. authenticate now takes an HTTPConnection (HTTP and WebSocket) and may get
    response / credential as None.
  • Dependencies: RequireIdentity (401, or close 1008 on WebSocket) and
    RequirePermission("resource", "action") (403), exported from tenxgraph_api with Identity.
    The route guard refuses to boot if a non-public route has neither.
  • Ownership in storage. core/threads.py::require_thread makes one scoped read before the run
    lock and any response header on every thread route and run entry point (REST, stream, WS, AG-UI,
    live, stop, observability). A foreign thread, message, memory, file or run answers 404. Reach
    widens reads, lists, deletes and stop; runs and writes always stay own.
  • GET /v1/me: user_id, tenant, roles, permissions, reach; {"auth": "off"} with
    auth off.
  • Jobs: the worker rebuilds config["user"] with the current role config before every run and
    checks tasks:trigger; refused or invalid identities fail the run without executing it. A task
    label identity="<name>" runs as a system identity from auth.system_identities and never
    replaces a caller's identity. /v1/tasks GET and cancel follow reach; trigger and PATCH stay own.
  • Keys with tenant: run caps, idempotency, rate limit by user and the WebSocket cap are keyed by
    user id plus tenant (run_limits.identity_key).
  • Removed: authorization.py, auth_backend.py, identity_cache.py, ownership_resolver.py,
    the ownership cache, tombstones, POST /v1/threads/{id}/messages (history import) and the
    authz reserved config key. request_config.py and client_filters.py moved to
    core/validation/.

Breaking changes and migration

Before Now
"authorization": "ownership" Delete it. Every resource is private to its owner (reach own).
"authorization": {"backend": "rbac", "roles": {...}} auth.roles, each role with permissions and reach
"authorization": "module:MyBackend" "auth": {"method": "custom", "path": "module:MyAuth"}; override authorize on the BaseAuth class (call super().authorize to keep roles)
authorize(user, resource, action, resource_id) authorize(identity, "resource:action", resource)
graph_introspection Delete it; reading the graph is graph:read
checkpointer:read/write/delete threads:read/write/delete
tasks:invoke tasks:trigger (tasks:pause removed)
checkpointer:import, graph:setup Removed
403 for a foreign resource 404; 403 now means only a missing permission
config["user"] as a dict The core Identity (identity.get(key) / identity[key] still work)

Testing

  • New tests: RBAC resolution (with hypothesis), auth config validation, transport parity (REST,
    stream, WS, AG-UI, live, files pass the resource id), thread ownership and 404s, media lifecycle,
    run guard recheck, rate-limit keying, jobs worker identity rebuild, system identities and task
    reach (tests/unit_tests/jobs/test_auth.py).
  • pre-commit run --all passes (ruff, mypy, bandit).
  • Full suite: <fill in: N passed, coverage>

Notes for reviewers

  • Risky areas: core/auth/permissions.py (authorize_resource, identity reuse with the rate
    limiter), core/threads.py::require_thread, routers/graph/run_guard.py (recheck after the
    lock), jobs/middleware.py::_run_identity (fails closed), checkpointer_service.py
    (file release on delete).
  • Release: the 10xgraph floor in pyproject.toml must point to the core release that ships
    Identity.
  • Docs (10xgraph-docs, docs/auth-rbac), client me() and the playground (feat/auth-me in each)
    follow in their own PRs.
  • CHANGELOG: older unreleased authorization.isolation / isolation_scope / rbac backend entries
    are superseded and will be trimmed before release.

- Implemented the /v1/me endpoint in the me.py router to return the authenticated user's identity, roles, and permissions.
- Added tests for the /v1/me endpoint to verify correct behavior with and without authentication.

test: add CLI tests for auth rules validation

- Created tests for the AuditCommand and BuildCommand to validate shared auth rules in the CLI.
- Included assertions for expected outputs and exit codes based on different configurations.

test: add integration tests for transport parity

- Developed integration tests to ensure consistent permission decisions across different transport methods (HTTP and WebSocket).
- Verified that revoked tokens and unauthorized access are handled correctly.

test: add unit tests for auth configuration validation

- Implemented unit tests for the AuthConfig validation logic to ensure proper handling of various auth configurations.
- Included tests for RBAC rules, tenant claims, and custom authorization methods.

test: add unit tests for auth dependencies

- Created unit tests for RequirePermission and RequireIdentity dependencies to ensure correct behavior in HTTP and WebSocket contexts.
- Verified that unauthorized access returns appropriate status codes.

test: add unit tests for /v1/me route

- Added unit tests for the /v1/me route to check identity retrieval and response structure.
- Ensured that the endpoint behaves correctly when authentication is off.
- Renamed `verified_user_id` to `verified_caller_key` to better reflect its purpose of identifying callers with tenant values.
- Updated the logic in `verified_caller_key` to handle tenant claims and return appropriate identity keys.
- Modified `client_key_for` to utilize the new `verified_caller_key` for user identification.
- Adjusted various routes and services to accommodate the new authorization structure, ensuring that tenant-specific access is enforced.
- Enhanced error handling in the `CheckpointerService` to manage thread deletions and file cleanup more effectively.
- Added tests to validate the new authorization logic, ensuring that users are correctly identified across different tenants.
- Updated integration and unit tests to reflect changes in the rate limiting and authorization mechanisms.
…and code

- Updated API/CLI SDK documentation to reflect changes in authentication and authorization handling, including the removal of the top-level `authorization` key.
- Revised `api-configuration.md` to clarify the new structure of the `auth` object, incorporating roles and permissions.
- Enhanced `auth-and-authorization.md` with detailed explanations of the new RBAC keys and their configurations.
- Adjusted CLI command documentation to align with the new authentication setup.
- Modified memory and store documentation to specify the new reach definitions and isolation scopes.
- Updated tests to ensure compliance with the new authentication and authorization logic, including role-based access control.
- Removed references to deprecated keys in request configuration validation.
- Updated the 10xgraph dependency to the commit hash 32d6bd45a02bef9ae0ab65e443e6a567716b9d7a.
- Added hypothesis package version 6.170.15 with its dependencies.
- Included sortedcontainers package version 2.4.0 as a dependency for hypothesis.
@Iamsdt
Iamsdt merged commit c7bdef1 into main Nov 8, 2026
9 checks passed
@Iamsdt
Iamsdt deleted the feat/auth-rbac branch November 8, 2026 03:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant