Agent Session Authorization#
Overview#
Agent (chat) sessions use ownership-based access control — not the general trace/session DSL filter — enforced at both the GraphQL and REST layers. The core rule is: non-admin users see only their own sessions; admins see all by default but can opt into viewer-only scoping when needed.
GraphQL Layer#
Permission Class: CanAccessAgentSession#
Defined in src/phoenix/server/api/agent_helpers.py (introduced in PR #14680):
- Non-admin: access denied if
owner_id != viewer_id - Admin: unrestricted access (all sessions visible)
All fields on the AgentSession GraphQL type (title, created_at, updated_at, user, first_input, latest_output, messages) are gated by permission_classes=[CanAccessAgentSession]. See src/phoenix/server/api/types/AgentSession.py.
List Query Scoping: get_agent_session_owner_filter()#
Also in agent_helpers.py, get_agent_session_owner_filter() generates the SQL predicate used in list queries:
- Non-admin: returns
WHERE user_id == viewer_id - Admin: returns
None(no filter — full visibility)
viewerOnly Argument on agentSessions#
After PR #14898 (resolves bug #14897), the agentSessions GraphQL query gained a viewerOnly: Boolean! = false argument :
viewerOnly | Auth enabled | Result |
|---|---|---|
false (default) | Yes | Admins see all; members see own |
true | Yes | All roles see only their own sessions |
| either | No | All persisted sessions returned |
The chat window session picker passes viewerOnly: true; the settings admin table uses the default (false), preserving admin-wide visibility. This prevents other users' sessions from being downloaded into the Relay store when opening the chat panel.
Schema change in app/schema.graphql .
REST Layer#
Routes added in src/phoenix/server/api/routers/agents.py (PR #14711):
| Endpoint | Behavior |
|---|---|
GET /agents/{agent_id}/sessions | List persisted (non-temporary, non-expired) sessions. With auth: scoped to user_id == request_user. Without auth: all persisted sessions. |
GET /agents/{agent_id}/sessions/{session_id} | Fetch single session. Returns 404 if expired or owned by another user. |
The helper _get_request_user_id(request) extracts the authenticated user ID or returns None when auth is disabled.
Role Model#
Roles are SYSTEM | ADMIN | MEMBER . The require_admin dependency enforces admin-only endpoints elsewhere in the API (e.g., users and user_api_keys queries) — but agent session routes use ownership scoping instead of a blanket admin gate, since members need to access their own sessions.
Key Files#
| File | Purpose |
|---|---|
src/phoenix/server/api/agent_helpers.py | CanAccessAgentSession permission class; get_agent_session_owner_filter() |
src/phoenix/server/api/types/AgentSession.py | AgentSession GraphQL type with per-field CanAccessAgentSession enforcement |
src/phoenix/server/api/queries.py | Root Query type; agentSessions field with viewerOnly arg |
src/phoenix/server/api/routers/agents.py | REST routes for session listing/retrieval with ownership scoping |
tests/integration/auth/test_auth.py | Integration tests: member API key sees only own sessions; 404 on others' sessions |
Related PRs & Issues#
- PR #14680 — Introduced
CanAccessAgentSession,get_agent_session_owner_filter(), and the settings admin table - PR #14711 — Added REST session routes with ownership scoping and CLI session picker
- Issue #14897 — Bug: admin session dropdown shows every user's sessions
- PR #14898 — Fix:
viewerOnly: truefor chat picker; scopes admin view to own sessions in picker context