Skip to main content

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

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..."
}
ClaimDescription
subSubject — the unique user ID
emailThe user's email address
email_verifiedWhether the user has verified their email
project_idThe Protekt project this token belongs to
iatIssued at — Unix timestamp of when the token was created
expExpiry — Unix timestamp after which the token is invalid
jtiJWT 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.

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