How to handle authz and authn when already using self-hosted deployment ? · Issue #137 · temporalio/temporal-proxy · GitHub
Skip to content

How to handle authz and authn when already using self-hosted deployment ? #137

Open

Description

@vpo-cdc

Hello,

I'm using self-hosted deployment with a custom server extended with claimmapper which only extracts subject (either user or service but those are different JWT providers) from JWTs and authorizer which uses a custom RBAC from a YAML file with a difference on cluster permissions and namespace permissions.

If using the proxy, should I implement the external decision endpoint with this logic and removes it from Temporal Server ? Or is it both duplicated in the external decision endpoint and the self hosted deployment ?

Activity

  1. pseudomuto commented on Aug 26, 2026

    @pseudomuto
    Collaborator

    Hey @vpo-cdc! The short answer is that the proxy doesn't force either arrangement. It's a routing/decision layer, not a replacement for the server's authorizer, so where the policy lives is entirely your call.

    However, I can think of a few scenarios that might apply to your situation:

    Leave it as is

    Don't configure the proxy's auth block; let caller metadata pass through to the Temporal frontend. ClaimMapper and Authorizer continue to work without any changes. This is a safe bet, as the proxy can only enforce on traffic that goes through it and only for the services it forwards (WorkflowService and OperatorService by default). The Web UI, a temporal CLI pointed straight at the frontend, and anything else that reaches the frontend directly all bypass it.

    Move it to the proxy

    Configure auth.external to point to an extension server that runs your claim mapping and sets authz info (see examples/authz). Be sure to set credentialHeaders: [authorization] to avoid forwarding the original JWT to the frontend. This requires that access to the frontend is gated by the proxy (i.e. can't bypass it).

    Proxy and frontend

    Same extension server, but leave credentialHeaders empty. The extension server still sees the caller's headers (they arrive as forwarded metadata rather than in AuthRequest.credentials), and nothing is stripped, so the server re-checks. This could be a reasonable way to handle having multiple upstream frontends. You could have general-purpose authn/authz handled at the proxy so you can reject early, but forward the original JWT to the specific frontend for deeper inspection/mapping.

    Finally, split it (see note)

    Claim mapper in the proxy, authorizer in the frontend. This is the one that would let you take JWT handling (ClaimMapper) off the server entirely while leaving the RBAC (Authorizer) where it is. Making it work would involve adding a principal on the response and stamping it on the forwarded request as a trusted header, with a small ClaimMapper on the frontend that reads that header instead of the token.

    The main constraint is that the trusted header becomes a bearer of authority, so the proxy has to strip any caller-supplied copy (the machinery for that already exists), and your frontend has to be unreachable except through the proxy. Anyone who can set the header on a direct connection is whoever they claim to be. This could be mitigated with a shared secret (e.g. HMAC headers), but is worth paying special attention to.

    Important

    Currently not possible because:

    • AuthResponse carries a decision and a reason and nothing else, so your extension server has no way to hand-map claims back to the proxy
    • The proxy has no hook for attaching per-caller metadata to the stream it forwards, since its only effect on that stream today is removing the credential headers you declared.

    If there's enough demand, we might consider adding this in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions