Skip to content

PostgREST JWT Authentication

Beau Barker edited this page Nov 5, 2025 · 32 revisions

Implement secure JWT-based authentication in SuperStack.

An /rpc/login PostgREST endpoint issues an access token.

1. JWT Secret

Generate a secret:

Note

PostgREST can share the JWT secret with Caddy. If you've already created a JWT secret for Caddy, use that.

openssl rand -base64 32

Put the secret in the environment file:

app/.env

# JWT secret used by both Caddy and PostgREST
JWT_SECRET=(your secret)

Add the secret and other settings to the Compose file:

app/compose.yaml

caddy:
  environment:
    JWT_SECRET: ${JWT_SECRET:?}

postgrest:
  environment:
    PGRST_APP_SETTINGS_JWT_EXP: 3600 # PostgREST default is no expiry!
    PGRST_JWT_SECRET: ${JWT_SECRET:?}
    PGRST_JWT_SECRET_IS_BASE64: true

postgres:
  environment:
    JWT_SECRET: ${JWT_SECRET:?}

2. Caddy

Caddyfile

Split your Caddyfile into sections, Public (no auth) and JWT, where a valid access token is required:

app/caddy/Caddyfile

{$CADDY_SITE_ADDRESS}

# --- Public ---

# PostgREST's public RPC endpoints
@auth {
  path /rpc/login /rpc/logout /rpc/refresh_token
}
handle @auth {
  reverse_proxy http://postgrest:3000
}

# PostgREST's OpenAPI endpoint
handle_path /rest/ {
  reverse_proxy http://postgrest:3000
}

# --- JWT Protected Routes ---

route {

  # Set the Authorization header from the Cookie header (for PostgREST)
  # Only if it's not already set
  @noHeader not header Authorization *
  route @noHeader {
    request_header Authorization "Bearer {cookie.access_token}"
  }

  # Non-public PostgREST endpoints
  handle /rpc/* {
    reverse_proxy http://postgrest:3000
  }

  handle_path /rest/* {
    reverse_proxy http://postgrest:3000
  }

  # .. Other private endpoints ..

}

Restart Caddy for the changes to take effect:

docker compose up -d --force-recreate caddy

Clone this wiki locally