JWT Authentication¶
Use JWTs when an end user is signed in and your app is acting on their behalf. Examples: a browser dashboard, a desktop client with sign-in, a mobile app. If your code is acting as itself (no end user), use API keys instead.
The Griddy API verifies tokens issued by Clerk, but the auth class is IdP-agnostic. Anything that signs RS256 JWTs against a published JWKS endpoint works.
What the API expects¶
Every authenticated request carries:
The token must:
- Be signed with RS256 against a key in the configured JWKS endpoint.
- Have an
issclaim matching the configured issuer. - Have an
audclaim matching the configured audience. - Not be expired (
expclaim). - Carry a
permissionsclaim covering whatever the endpoint requires. - Optionally have an
azpclaim if the deployment has configured authorized parties.
If any of these fail, the API returns 401. See
Errors & Troubleshooting for the specific messages.
Getting a token¶
From a Clerk-authenticated frontend (most common)¶
In a React or Next.js app that's already wired to Clerk, ask the SDK for a session token:
import { useAuth } from "@clerk/clerk-react";
// or "@clerk/nextjs", "@clerk/clerk-expo", etc.
function MyComponent() {
const { getToken } = useAuth();
async function callApi() {
const token = await getToken();
const response = await fetch("/api/v1/leagues/", {
headers: { Authorization: `Bearer ${token}` },
});
return response.json();
}
}
getToken() returns the default session token — that's what the API
expects. Don't pass template unless you've created a custom JWT
template for a third-party integration like Supabase or Firebase.
From a server-side context (backend code, scripts)¶
If your code is running outside a browser but you still want a JWT (rather than an API key), Clerk's Backend API can mint a session token for an existing user:
# 1. Create a session for the user.
curl -X POST https://api.clerk.com/v1/sessions \
-H "Authorization: Bearer $CLERK_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"user_id": "user_2abcDEF..."}'
# → returns {"id": "sess_xxx", ...}
# 2. Mint a token for that session.
curl -X POST https://api.clerk.com/v1/sessions/sess_xxx/tokens \
-H "Authorization: Bearer $CLERK_SECRET_KEY"
# → returns {"jwt": "eyJ..."}
Two important constraints:
- Direct session creation is rejected by Clerk production instances. The pattern above works on dev instances; in production, your backend should obtain tokens through a real sign-in flow, not by impersonating users via the Backend API.
- Session tokens default to ~60 second TTL. Don't cache them; mint fresh ones per request or per short batch.
If you find yourself reaching for this pattern in production, that's a strong signal you should be using an API key instead.
Where to configure custom claims¶
If you need additional data on the token (a permissions array, custom
flags, organization metadata), add it under
Sessions → Customize session token in the Clerk dashboard. Don't use
the JWT Templates section — those are for third-party integrations that
need a different token shape, and they have higher latency.
Suggested permissions claim, sourced from user public metadata:
Or for organization-based RBAC:
The Griddy API accepts the claim as either a list of strings or a single space-delimited string.
Lifecycle¶
Clerk session tokens have a short TTL (~60s by default). The browser
SDKs handle refresh automatically; you just call getToken() and get
a current one. Server-side users of the Backend API should mint fresh
tokens per request rather than caching.
When a user signs out of your frontend, their session is invalidated on Clerk's side and any tokens previously issued from it become unusable.
Troubleshooting¶
See Errors & Troubleshooting for full coverage. The most common JWT-specific issues:
Token has expired— Clerk session tokens are ~60s; mint a fresh one.Invalid audience.— yourauddoesn't match the API's expected value. Check what audience your dashboard's session token is issuing with.Invalid authorized party.— backend-API-minted tokens have noazpclaim. Either don't configure authorized parties for that environment or use a frontend-issued token.