Concepts
Verifying tokens
Checking an access token on your backend, in any language.
A ZevAuth access token is an ordinary JWT. You verify it with your environment’s public key, which we publish. Your backend never calls us to check a token, so an outage of ours is not an outage of yours.
Discovery
Every environment publishes an OIDC discovery document:
https://api.zevauth.net/v1/{environmentId}/.well-known/openid-configuration
It gives you the issuer and the jwks_uri. Read both from there rather than
building them yourself. The issuer is an identity, not a URL you can assume,
and a self-hosted deployment will not match a guess.
Node
import { verifyZevAuthToken } from '@zevauth/nextjs/server';
const claims = await verifyZevAuthToken(token, {
environmentId: process.env.ZEVAUTH_ENVIRONMENT_ID!,
});
Or with jose directly, in any framework:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const issuer = `https://api.zevauth.net/v1/${environmentId}`;
const jwks = createRemoteJWKSet(new URL(`${issuer}/.well-known/jwks.json`));
const { payload } = await jwtVerify(token, jwks, {
issuer,
audience: environmentId,
});
Python
from jwt import PyJWKClient
import jwt
issuer = f"https://api.zevauth.net/v1/{environment_id}"
jwks = PyJWKClient(f"{issuer}/.well-known/jwks.json")
claims = jwt.decode(
token,
jwks.get_signing_key_from_jwt(token).key,
algorithms=["RS256"],
issuer=issuer,
audience=environment_id,
)
Always check the audience
Cache the key set
Fetch the JWKS once and keep it. Libraries like jose and PyJWKClient do
this for you, and refetch when they meet a key id they do not know, which is
what a key rotation looks like. Building a new client per request defeats the
cache and fetches the document on every call.
Retiring keys stay published for a while after a rotation, so tokens signed a minute before it keep verifying for the rest of their life.
Updated at, Friday, August 28, 2026