JWT tokens
Protekt uses JSON Web Tokens (JWTs) as the primary mechanism for representing and verifying user identity. This page explains how JWTs work, what Protekt puts inside them, and how to validate them in your application.
What is a JWT?
A JWT is a compact, URL-safe string that encodes a set of claims: statements about a user or session, and is cryptographically signed so that the recipient can verify it hasn't been tampered with.
A JWT has three parts separated by dots:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOWtsYWJjIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
^-- Header ^-- Payload ^-- Signature
Header
The header specifies the token type and the signing algorithm:
{
"alg": "RS256",
"typ": "JWT"
}
Protekt signs tokens using RS256 (RSA + SHA-256), an asymmetric algorithm. Protekt holds the private key to sign tokens; your application uses Protekt's public key to verify them.
Payload (claims)
The payload contains the user's data. Protekt includes the following standard and custom claims:
{
"sub": "usr_9klabc",
"email": "user@example.com",
"email_verified": true,
"project_id": "proj_01jk8abc",
"iat": 1710844800,
"exp": 1710848400,
"jti": "tok_xm8q..."
}
| Claim | Description |
|---|---|
sub | Subject — the unique user ID |
email | The user's email address |
email_verified | Whether the user has verified their email |
project_id | The Protekt project this token belongs to |
iat | Issued at — Unix timestamp of when the token was created |
exp | Expiry — Unix timestamp after which the token is invalid |
jti | JWT ID — a unique identifier for this token, used for revocation |
You can also include custom claims by adding data to the user's metadata field. Protekt will forward top-level metadata keys as additional claims.
Signature
The signature is generated by encoding the header and payload with Base64URL, joining them with a dot, and signing the result with Protekt's private key. This ensures:
- The token was genuinely issued by Protekt
- The contents haven't been altered since it was signed
Token verification
Never trust a JWT without verifying its signature. Protekt provides two ways to verify tokens.
Option 1: SDK verification (recommended)
The simplest approach — the SDK handles signature verification, expiry checking, and claim validation automatically:
const { user, error } = await protekt.auth.verifyToken(token);
if (error) {
// Token is invalid, expired, or revoked
}
console.log(user.id); // usr_9klabc
console.log(user.email); // user@example.com
Option 2: local verification with the public key
For high-throughput scenarios where you want to avoid a network call on every request, you can verify tokens locally using Protekt's public JWKS endpoint:
GET https://auth.protekt.io/v1/.well-known/jwks.json
Using a library like jose:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://auth.protekt.io/v1/.well-known/jwks.json')
);
async function verifyToken(token) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://auth.protekt.io',
audience: 'proj_01jk8abc',
});
return payload;
}
The JWKS endpoint is cached and rotated periodically. Your JWKS client should cache the keys and retry with a fresh fetch when an unknown kid is encountered.
Token expiry
Protekt access tokens are short-lived — they expire after 1 hour by default. This limits the damage if a token is compromised. You can adjust the expiry per project in the dashboard or via the Account API.
When a token expires, your application should use the refresh token to obtain a new one silently. See Session Management for details.
Token revocation
JWTs are stateless by design — once issued, they remain valid until expiry. Protekt maintains a revocation list (blocklist) to support immediate invalidation. When you call logout or revoke a token via the API, Protekt adds its jti to the blocklist.
SDK verification and the /auth/token/verify endpoint check this list automatically. Local JWKS verification does not — if you verify tokens locally, call POST /auth/token/verify for sensitive operations like privilege changes.
Security considerations
- Never decode a JWT and trust its contents without verifying the signature first. The payload is Base64-encoded, not encrypted, so anyone can read it.
- Do not store sensitive data in JWT claims. Claims are readable by the client. Use metadata only for non-sensitive identifiers.
- Keep access tokens short-lived. A one-hour expiry is a reasonable default. Avoid lifetimes longer than 24 hours.
- Rotate your API keys if you suspect they have been compromised, this does not invalidate existing user JWTs but prevents new ones from being issued via the affected key.
Next steps
- Session Management — refresh tokens and session lifecycle
- Authentication Flow — how JWTs move through your app
- Token API Reference — verify, refresh, and revoke endpoints