temporalio / temporalio/temporal

ui-server: ListNamespaces/GetClusterInfo return 403 for namespace-scoped admin tokens (requires undocumented "temporal-system:" permission)

Open
#11,639 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

potential-bug
Dominant language
Go
Stars
23.2k
Forks
1.9k
Avg merge
2d 8h
Merged PRs (30d)
228

Description

Expected Behavior

A JWT-authenticated user with a namespace-scoped admin permission (via the default JWT claim mapper / default authorizer, e.g. "permissions": ["my-namespace:admin"]) should be able to load the Temporal Web UI without authorization errors, since they are a full admin of that namespace.

Actual Behavior

ui-server's own backend calls to ListNamespaces and GetClusterInfo (issued on every page load to populate the namespace list / version banner) return 403 Request unauthorized / PermissionDeniedFailure — even for a user holding admin on every namespace that exists. This is not obvious from the docs: a token that is a full admin of all namespaces still cannot use the UI at all, because these two specific calls are gated on a separate, undocumented permission scope.

Steps to Reproduce the Problem

  1. Start temporal-server with global.authorization configured: jwtKeyProvider pointing at a JWKS, claimMapper: "default", authorizer: "default", permissionsClaimName: "permissions".
  2. Issue a JWT (correct kid matching the JWKS) with "permissions": ["my-namespace:admin"]. Call DescribeNamespace on my-namespace with this token — succeeds, as expected for an admin.
  3. Call ListNamespaces or GetClusterInfo with the same token (directly against temporal-server, or via ui-server configured with auth.enabled: false and temporalGrpcAddress pointed at the server, passing the token as an Authorization: Bearer header) — returns 403 Request unauthorized, regardless of the namespace-scoped admin role.
  4. Reissue the token with "permissions": ["my-namespace:admin", "temporal-system:read"] and repeat step 3 — now returns 200 OK.

Specifications

  • Version: temporal-server 1.31.2, ui-server 2.53.3
  • Platform: N/A (reproducible with local/dev persistence, no external dependencies)

Root Cause

ListNamespaces and GetClusterInfo are Scope: Cluster APIs. DefaultAuthorizer.Authorize only consults claims.System for Scope: Cluster requests — claims.Namespaces[...] is never checked for these, no matter the role held there. DefaultJWTClaimMapper only ever populates claims.System from a permission entry whose namespace segment is exactly the literal temporal-system (i.e. primitives.SystemLocalNamespace) — any other namespace-scoped permission, including admin on every namespace individually, has no effect on claims.System.

The result: any deployment using only namespace-scoped permissions (the natural/expected shape for the namespace:role permission format the docs describe) can never satisfy ListNamespaces/GetClusterInfo, so the Web UI is unusable for any user unless they are separately granted the specific, non-obvious temporal-system:read (or higher) permission — a requirement that does not appear to be documented in the JWT/authorization docs.

Suggested Fix / Ask

Either (a) document explicitly that temporal-system:<role> is required for any UI-facing token, since ui-server depends on ListNamespaces/GetClusterInfo internally, or (b) treat admin on the reachable/queried namespace as sufficient for ui-server's own calls, or (c) have ui-server degrade gracefully (skip cluster info / infer the namespace list from what is reachable) instead of surfacing a hard 403 when it lacks system-level read access.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing ListNamespaces and GetClusterInfo through DefaultAuthorizer.Authorize and DefaultJWTClaimMapper, then inspect how ui-server invokes these calls. Reproduce the 403 with the namespace-scoped token described in the issue and verify the chosen behavior: documented temporal-system access, authorization through reachable namespaces, or graceful UI degradation.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authorization, backend-api-design
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.