Skip to content

feat(auth): add OIDC claim-based authorization (allowedClaimKey/allowedClaimValues)#3594

Open
brucearctor wants to merge 1 commit into
temporalio:mainfrom
brucearctor:feat/oidc-claim-authorization
Open

feat(auth): add OIDC claim-based authorization (allowedClaimKey/allowedClaimValues)#3594
brucearctor wants to merge 1 commit into
temporalio:mainfrom
brucearctor:feat/oidc-claim-authorization

Conversation

@brucearctor

Copy link
Copy Markdown

Description & motivation 💭

Closes #3562

Currently, the Temporal UI allows any user who can authenticate with the configured OIDC provider to access the UI. This is a security gap when the UI is externally exposed and the OIDC provider serves a large tenant (e.g., an entire Azure AD / Entra ID directory).

This PR adds claim-based authorization — two new AuthProvider config fields that let operators restrict UI login to users whose ID token contains specific claim values (e.g., a group membership or role).

New Config Fields

auth:
  providers:
    - # ... existing fields ...
      allowedClaimKey: groups            # claim to check
      allowedClaimValues:                # allowlist of values
        - temporal-admins

Docker Env Vars

TEMPORAL_AUTH_ALLOWED_CLAIM_KEY=groups
TEMPORAL_AUTH_ALLOWED_CLAIM_VALUES=temporal-admins,temporal-ops

Key Design Decisions

  • Claim check at login only. After ExchangeCode succeeds but before SetUser, the callback inspects the ID token payload. If the user lacks the required claim, login is rejected with 403. Most IdPs don't update group claims on token refresh, so re-checking on refresh would give false confidence. maxSessionDuration already bounds the window.
  • Supports nested claims via dot notation (e.g., realm_access.roles for Keycloak) and literal keys containing colons (e.g., cognito:groups for AWS Cognito) or dots (e.g., Auth0 URL-namespaced claims).
  • Both string and array claims are supported. For arrays, the user is authorized if ANY value matches ANY allowedValue.
  • Generic 403 response — the HTTP body says "access denied: insufficient permissions" without leaking which claim is checked. Detailed reason is server-logged only.
  • Fully backward compatible — if neither field is set, behavior is identical to today.

Design Considerations 🎨

None — this is purely server-side config with no UI changes.

Testing 🧪

  • Unit tests added

How was this tested 👻

16 new unit tests in server/server/auth/auth_test.go covering:

  • No allowlist configured (noop) — both empty key and empty values
  • Flat string claims — match and no-match
  • Array claims — single match, multi-allowed, no match
  • Nested dot-notation — 2-level and 3-level deep
  • Missing claims and missing nested paths
  • Colon-containing keys (cognito:groups)
  • URL-namespaced keys with dots (https://myapp.example.com/roles)
  • Malformed JWT input

All existing tests pass. go vet clean on all modified packages.

Steps for others to test: 🚶🏽‍♂️🚶🏽‍♀️

  1. Configure allowedClaimKey: groups and allowedClaimValues: ["test-group"] in a provider
  2. Start with pnpm dev:with-auth (the mock OIDC server)
  3. Login — if the mock token includes groups: ["test-group"], access is granted; otherwise 403

Checklists

Merge Checklist

  • Code compiles (go vet clean)
  • All existing tests pass
  • New unit tests added (16 cases)
  • Documentation updated (AUTHENTICATION.md)
  • Docker config template updated (docker.yaml)
  • Config validation added (both fields must be set together)
  • No breaking changes

Issue(s) closed

Closes #3562

Docs

Any docs updates needed?

AUTHENTICATION.md has been updated with:

  • Provider settings reference table (2 new fields)
  • New "Claim-Based Authorization" section with examples for Azure AD/Entra ID, Keycloak, Auth0, and AWS Cognito
  • Docker env var documentation
  • Troubleshooting entry for 403 errors after login

@brucearctor brucearctor requested a review from a team as a code owner June 24, 2026 03:29
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

@brucearctor is attempting to deploy a commit to the Temporal Team on Vercel.

A member of the Team first needs to authorize it.

…edClaimValues)

Add support for restricting UI access based on OIDC ID token claims.
Operators can now configure allowedClaimKey and allowedClaimValues on
an auth provider to gate login to users with specific group memberships,
roles, or other claim values.

Features:
- New AuthProvider config fields: allowedClaimKey, allowedClaimValues
- Supports flat string claims, array claims, and nested dot-notation
  paths (e.g., realm_access.roles for Keycloak)
- Supports literal keys containing colons (e.g., cognito:groups)
  and dots (e.g., URL-namespaced Auth0 claims)
- Docker env vars: TEMPORAL_AUTH_ALLOWED_CLAIM_KEY,
  TEMPORAL_AUTH_ALLOWED_CLAIM_VALUES (comma-separated)
- Config validation: both fields must be set together
- Generic 403 error response (no claim metadata leaked to clients)
- 16 new unit tests covering all claim types and edge cases

Backward compatible: if neither field is set, behavior is unchanged.

Closes temporalio#3562
@brucearctor brucearctor force-pushed the feat/oidc-claim-authorization branch from d26cf84 to 6b11ad1 Compare June 24, 2026 03:39
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.

Restrict UI OIDC login to users with a specific group/role claim (authorization, not just authentication)

1 participant