ZevAuth Docs
Sign up

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