Skip to content
Beau Barker edited this page Dec 12, 2025 · 17 revisions

A service JWT is a token that authenticates a backend service — not a human user.

Use it when one internal component (for example, an api endpoint or background job) needs to securely talk to another (like PostgREST or the API gateway) without going through login or cookies.

Create a JWT

You can use this script to generate a service JWT:

base64-to-jwt.sh

#!/usr/bin/env bash
#
# base64-to-jwt.sh — Create an HS256 JWT using a base64-encoded secret key.
#
# Usage:
#   ./base64-to-jwt.sh <base64_secret> [payload_json] [algorithm]
#
# Example:
#   ./base64-to-jwt.sh 'rzIw/CmtdWD9IaNgGigcaFFFuBDTbJ4ZyCkZvWmSrqk=' \
#     '{"role":"service","sub":"service"}'
#
#   ./base64-to-jwt.sh "$JWT_SECRET" '{"role":"service"}' HS512
#
set -euo pipefail

SECRET_B64="${1:-}"
PAYLOAD_JSON=${2:-'{"role":"service","sub":"service"}'}
ALG="${3:-HS256}"

if [[ -z "$SECRET_B64" ]]; then
  echo "Usage: $0 <base64_secret> [payload_json] [algorithm]" >&2
  exit 1
fi

# --- Build header/payload base64url ---
b64url() {
  openssl base64 -e -A | tr -d '\n' | tr '+/' '-_' | tr -d '='
}

HEADER=$(printf '{"alg":"%s","typ":"JWT"}' "$ALG" | b64url)
PAYLOAD=$(printf '%s' "$PAYLOAD_JSON" | b64url)
DATA="$HEADER.$PAYLOAD"

# --- Decode base64 secret to hex for OpenSSL ---
KEY_HEX=$(printf '%s' "$SECRET_B64" | base64 -d | xxd -p -c256)

# --- Sign and produce signature in base64url ---
SIG=$(printf '%s' "$DATA" \
  | openssl dgst -binary -sha256 -mac HMAC -macopt hexkey:"$KEY_HEX" \
  | b64url)

echo -n "$DATA.$SIG"

Run it:

chmod +x base64-to-jwt.sh
./base64-to-jwt.sh your-secret
eyJhbGciOiJIUzI1NiIsIn...

Grab the generated token and put it into .env:

app/.env

SERVICE_JWT=eyJhbGciOiJIUzI1NiIsIn...

Add it to services that need it:

app/compose.yaml

api:
  environment:
    SERVICE_JWT: ${SERVICE_JWT:?}

PostgREST

Create a role and grant permissions.

db/postgres/migrations/90-service_role.sql

create role service noinherit nologin;

grant service to authenticator;
grant usage on schema api to service;
grant select, insert, update on table api.account to service;

Clone this wiki locally