<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep" -->

---
title: Temporal JWT token authentication | daily.dev
description: This guide configures JWT authentication for self-hosted Temporal clusters using Keycloak as the identity provider. It covers two flows: Client Credentials for...
canonical: https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Temporal JWT token authentication | daily.dev
og:description: This guide configures JWT authentication for self-hosted Temporal clusters using Keycloak as the identity provider. It covers two flows: Client Credentials for...
og:url: https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep
og:image: https://api.daily.dev/og/posts/iGUCzsDEP.png
og:image:alt: Temporal JWT token authentication
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Temporal JWT token authentication

**[Temporal](https://daily.dev/sources/temporal)** · 13 min read · 0 upvotes · 0 comments

## Summary

This guide configures JWT authentication for self-hosted Temporal clusters using Keycloak as the identity provider. It covers two flows: Client Credentials for Temporal Workers and SDK apps, and Authorization Code with PKCE for human users accessing the Web UI and CLI. Keycloak can emit a 'permissions' claim as a flat array like 'temporal-system:admin' using a protocol mapper, since Temporal expects that format rather than Keycloak's nested 'resource_access' object. The post walks through setting environment variables such as TEMPORAL_AUTH_AUTHORIZER, TEMPORAL_AUTH_CLAIM_MAPPER, and TEMPORAL_JWT_KEY_SOURCE1, testing with the Temporal CLI and curl, and refreshing short-lived JWTs in a background loop for long-running Python SDK Workers. It notes the setup skips TLS, so tokens travel in plain text. This is the first of a three-part series on Temporal security.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://temporal.io/blog/temporal-jwt-token-authentication>

## Questions this post answers

### How do I format the JWT permissions claim so Temporal's default ClaimMapper can read it?

Temporal's default ClaimMapper expects a claim named 'permissions' containing a flat array of strings formatted as 'namespace:permission', where namespace is a namespace name or 'temporal-system' for cluster-wide scope, and permission is one of read, write, worker, or admin. Keycloak normally nests client roles inside a 'resource_access' object, so a custom protocol mapper (User Client Role type, multivalued, claim name 'permissions') is needed to flatten it into the expected array.

_Configuring claim mappers for Temporal auth? Track setup guides like this on daily.dev._

### How do I authenticate a Temporal Worker vs a human user differently with OAuth?

Temporal Workers and backend applications, considered confidential clients running server-side, authenticate using the OAuth 2.0 Client Credentials grant with a client_id and client_secret, since they don't represent a single human user. Human users accessing the Temporal Web UI or CLI instead use the Authorization Code Flow with PKCE, logging in via username and password through the identity provider's login screen.

_Developers wiring up Temporal auth flows can follow setups like this one on daily.dev._

### How do I keep a long-running Temporal Worker authenticated when JWT tokens expire?

Run a background loop that refreshes the JWT before it expires rather than using a static authorization header. The Python Temporal SDK allows updating a connected client's rpc_metadata without disrupting ongoing gRPC connections or restarting workers. One approach reads the 'expires_in' field from Keycloak's token response and sleeps for that duration minus a 30 second buffer before fetching a new token.

_Teams running long-lived Temporal workers can find token-refresh patterns like this on daily.dev._

## Similar posts on daily.dev

- [Kubernetes access via an identity provider: Public client, not confidential](https://daily.dev/posts/kubernetes-access-via-an-identity-provider-public-client-not-confidential-j18kxxmud) · CNCF · 0 upvotes · 0 comments
- [Integrate Keycloak with ASP.NET Core Using OAuth 2.0](https://daily.dev/posts/integrate-keycloak-with-asp-net-core-using-oauth-2-0-w348tdawg) · Milan Jovanović · 11 upvotes · 1 comments
- [How to Configure and Test OAuth with Keycloak and Bruno](https://daily.dev/posts/how-to-configure-and-test-oauth-with-keycloak-and-bruno-wk8x2govb) · Bruno Blog · 17 upvotes · 1 comments
- [Securing AI Agents with Keycloak: A Practical Guide](https://daily.dev/posts/securing-ai-agents-with-keycloak-a-practical-guide-i7mcwgebb) · gitconnected · 0 upvotes · 0 comments
- [Authentication Flow Explained: Step-by-Step Login & Token Exchange Process](https://daily.dev/posts/authentication-flow-explained-step-by-step-login-token-exchange-process-jhfu1vcn7) · Security Boulevard · 2 upvotes · 0 comments

---

Tags: [#infrastructure](https://daily.dev/tags/infrastructure), [#authentication](https://daily.dev/tags/authentication), [#jwt](https://daily.dev/tags/jwt)

[View this post on daily.dev](https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Temporal JWT token authentication","url":"https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep"},"datePublished":"2026-10-27T13:07:33.370Z","dateModified":"2026-10-27T13:08:07.593Z","description":"This guide configures JWT authentication for self-hosted Temporal clusters using Keycloak as the identity provider. It covers two flows: Client Credentials for...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/e88e1bcbb0f71fb1c790a41f07272354?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/e88e1bcbb0f71fb1c790a41f07272354?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Temporal","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Temporal","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/65290ca7e3ab46a7b1df76f2757800c2","url":"https://daily.dev/sources/temporal"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"infrastructure,authentication,jwt","timeRequired":"PT13M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Temporal","item":"https://daily.dev/sources/temporal"},{"@type":"ListItem","position":3,"name":"Temporal JWT token authentication"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/temporal-jwt-token-authentication-iguczsdep#faq","mainEntity":[{"@type":"Question","name":"How do I format the JWT permissions claim so Temporal's default ClaimMapper can read it?","acceptedAnswer":{"@type":"Answer","text":"Temporal's default ClaimMapper expects a claim named 'permissions' containing a flat array of strings formatted as 'namespace:permission', where namespace is a namespace name or 'temporal-system' for cluster-wide scope, and permission is one of read, write, worker, or admin. Keycloak normally nests client roles inside a 'resource_access' object, so a custom protocol mapper (User Client Role type, multivalued, claim name 'permissions') is needed to flatten it into the expected array. Configuring claim mappers for Temporal auth? Track setup guides like this on daily.dev."}},{"@type":"Question","name":"How do I authenticate a Temporal Worker vs a human user differently with OAuth?","acceptedAnswer":{"@type":"Answer","text":"Temporal Workers and backend applications, considered confidential clients running server-side, authenticate using the OAuth 2.0 Client Credentials grant with a client_id and client_secret, since they don't represent a single human user. Human users accessing the Temporal Web UI or CLI instead use the Authorization Code Flow with PKCE, logging in via username and password through the identity provider's login screen. Developers wiring up Temporal auth flows can follow setups like this one on daily.dev."}},{"@type":"Question","name":"How do I keep a long-running Temporal Worker authenticated when JWT tokens expire?","acceptedAnswer":{"@type":"Answer","text":"Run a background loop that refreshes the JWT before it expires rather than using a static authorization header. The Python Temporal SDK allows updating a connected client's rpc_metadata without disrupting ongoing gRPC connections or restarting workers. One approach reads the 'expires_in' field from Keycloak's token response and sleeps for that duration minus a 30 second buffer before fetching a new token. Teams running long-lived Temporal workers can find token-refresh patterns like this on daily.dev."}}]}
```

