Files
tasks/config/jwt/generate-token.sh
Vladyslav SimonenkoandGitHub 3081343d68 Add local JWT development server with key generation scripts (#9647)
## What changed?
- Added `development-jwt.yaml` config that runs Temporal with JWT
authorization enabled using a JWKS file loaded from local disk. Loading
from local disk is added in
https://github.com/temporalio/temporal/pull/9590.
- Added `config/jwt/setup-keys.sh` to generate an RSA key pair and JWKS
file in `/tmp/temporal-jwt-test/`.
- Added `config/jwt/generate-token.sh` to create signed JWTs for
testing.
  - Added `make start-jwt` target.
 
## Why?
Testing JWT authentication currently requires an external identity
provider or manual key/token setup. This provides a self-contained local
workflow: `make start-jwt` generates keys automatically and starts the
server with auth enforced, and `generate-token.sh` produces tokens on
demand.

## How did you test it?
- [X] built
- [X] run locally and tested manually
- [ ] covered by existing tests
- [ ] added new unit test(s)
- [ ] added new functional test(s)

Tested this in conjunction with
https://github.com/temporalio/temporal/pull/9582. Was able to list the
workflows by passing the token and dumping their history. When the token
is not used, the auth fails with "Request denied".

## Potential risks
Keys are stored in /tmp/temporal-jwt-test/ which is world-readable. This
is acceptable for local development but the scripts should not be used
in shared or production environments.
2026-04-15 16:26:53 -07:00

59 lines
1.9 KiB
Bash
Executable File

#!/bin/bash
# Generate a test JWT signed with the local private key
# Usage: ./generate-token.sh [subject] [permissions...]
#
# Examples:
# ./generate-token.sh # Default: [email protected] with system:admin
# ./generate-token.sh [email protected] # Custom subject with system:admin
# ./generate-token.sh [email protected] default:admin # Custom subject with namespace permission
# ./generate-token.sh [email protected] system:admin default:writer # Multiple permissions
#
# The generated JWT has the following structure:
# Header: {"alg": "RS256", "typ": "JWT", "kid": "test-key-1"}
# Payload: {"sub": "<subject>", "permissions": ["<perm1>", ...], "iat": <now>, "exp": <now+1h>}
set -euo pipefail
base64url() { base64 | tr -d '\n' | tr '+/' '-_' | tr -d '='; }
KEY_DIR="/tmp/temporal-jwt-test"
PRIVATE_KEY="$KEY_DIR/private-key.pem"
if [ ! -f "$PRIVATE_KEY" ]; then
echo "Private key not found at $PRIVATE_KEY, running setup-keys.sh..." >&2
"$(dirname "$0")/setup-keys.sh"
fi
if [ ! -f "$PRIVATE_KEY" ]; then
echo "Error: Private key still not found at $PRIVATE_KEY" >&2
exit 1
fi
SUBJECT="${1:-test[email protected]}"
shift 2>/dev/null || true
# Collect permissions (default to system:admin if none specified)
if [ $# -eq 0 ]; then
PERMISSIONS='["system:admin"]'
else
PERMISSIONS=$(printf '%s\n' "$@" | jq -R . | jq -s .)
fi
NOW=$(date +%s)
EXP=$((NOW + 3600))
HEADER='{"alg":"RS256","typ":"JWT","kid":"test-key-1"}'
HEADER_B64=$(echo -n "$HEADER" | base64url)
PAYLOAD=$(jq -n -c --arg sub "$SUBJECT" --argjson perms "$PERMISSIONS" \
--argjson iat "$NOW" --argjson exp "$EXP" \
'{sub: $sub, permissions: $perms, iat: $iat, exp: $exp}')
PAYLOAD_B64=$(echo -n "$PAYLOAD" | base64url)
SIGNATURE=$(echo -n "${HEADER_B64}.${PAYLOAD_B64}" | \
openssl dgst -sha256 -sign "$PRIVATE_KEY" | \
base64url)
# Output the complete JWT
echo "${HEADER_B64}.${PAYLOAD_B64}.${SIGNATURE}"