Private betaZynth Auth is currently in private beta testing.New organizations are created by invitation only, and no plan can be purchased yet.Request early access

Tokens & sessions

Zynth Auth issues two RS256-signed JWTs on sign-up, login, and refresh. The same token pair is issued regardless of how the user authenticated — password, password + a TOTP MFA challenge, or a passkey (WebAuthn). A user-verified passkey is two factors in one step, so it is not additionally challenged for a code.

Access token

Short-lived (default 15 minutes), sent as Authorization: Bearer <token> on each request. It carries minimal claims — identity, not permissions:

ClaimMeaning
subUser ID
tidTenant (organization) ID
sidSession ID — the refresh-token family this token belongs to; used for server-side revocation
typeaccess
issIssuer — zynth-auth, the access-token issuer. (The OIDC id_token uses a different issuer — see below.)
audAudience — zynth-services, the platform default. Tokens minted for a registered OAuth resource instead carry that resource's identifier and verify only there.
emailUser email — present on sign-up/login tokens; not re-included on tokens minted by /refresh
iat / expIssued-at / expiry
jtiUnique token ID

Treat every claim except sub/tid as convenience metadata. email in particular is a hint, not an authorization input — resolve identity and permissions server-side.

The JWT header carries a kid (key ID) identifying which signing key to verify against.

Permissions are not in the token. Roles/permissions are resolved server-side on each request so access changes take effect immediately. Don't make authorization decisions from token claims alone — treat the token as proof of identity.

Refresh token

Longer-lived (default 7 days for workforce users, 30 days for CIAM/customer users), used only to obtain a new token pair via POST /api/v1/auth/refresh. It has no aud and is revocable server-side. Refresh re-checks tenant membership before issuing new tokens.

By default the refresh token is returned in the JSON body — the right choice for SDK/API clients. Browser clients should instead send use_cookie: true on signup/login/mfa-challenge/change-password: the refresh token is then set as an httpOnly, Secure, SameSite=Strict cookie scoped to /api/v1/auth (and the body's refresh_token is null), so JavaScript — and therefore an XSS payload — can never read the long-lived credential. In cookie mode call refresh with an empty body; the cookie authenticates and is rotated on every refresh. See Cookie transport.

Rotation, reuse detection & revocation

Refresh tokens are rotated: every call to /refresh consumes the token you present and returns a brand-new refresh token. A refresh token is single-use — persist the new one and discard the old.

  • Reuse detection: presenting an already-used refresh token (a strong signal it was stolen) revokes the entire session — every token in it stops working and the user must sign in again.
  • Logout is immediate: POST /api/v1/auth/logout revokes the session server-side. Because access tokens are checked against the session on every request, the outstanding access token stops working at once, not only when it would have expired.
  • A password change or reset invalidates everything issued earlier. Every token — access and refresh, across all devices — with an iat before the password change is rejected. change-password returns a fresh pair so the current device stays signed in; after a reset-password the user logs in again.
  • Sign out everywhere / instant global revocation. A single lever — a per-user revocation epoch — invalidates every token on every device at once, with no session enumeration. It backs POST /auth/sign-out-all and also fires automatically on the directory-driven events below. The same instant kill applies whether the trigger is the user, your support team, or the tenant's identity provider.
  • Directory-driven revocation (enterprise). When a user is deprovisioned or logged out through the tenant's IdP, their Zynth sessions end immediately via that same lever: SCIM deactivation/deletion, SAML single logout, and OIDC back-channel logout all revoke on the spot. See Directory provisioning and the enterprise SSO guides.
  • The platform itself, in response to a security signal. Reuse detection above revokes the one session it caught. Separately, the autonomous-response engine can pull the global revocation lever for a user on a signal such as refresh-token reuse — every device, at once. It is off by default and never turns itself on: your organization starts at Manual for every response class, and acting without a human additionally requires a track record the class has to earn. But once you have raised a level, this is a way a user's sessions can end with no person having clicked anything, so it belongs on this list. Every such action is in the decision ledger, notifies when unattended, and can be undone — reverting restores the previous state, so devices resume working without a fresh sign-in.

Tokens minted by the OpenID Provider

When Zynth Auth acts as an OpenID Provider — signing users into your app — it mints two further RS256 JWTs, signed with the same JWKS keys but with deliberately different iss/aud values from the access token:

TokenissaudWhat it is
Access tokenzynth-authzynth-services or a resource identifierCredential for API calls — verified by resource servers, opaque to clients
id_tokenThe OIDC issuer URL (the discovery issuerhttps://auth.zynthmedia.com on the managed service)Your client_idProof of login for your app — carries sub, tid, amr, sid
logout_tokenThe OIDC issuer URLYour client_idSigned back-channel logout notification

The issuers never overlap: zynth-auth appears only in access tokens, the issuer URL only in id_tokens and logout_tokens. Validate each token type with its own configuration — a shared verifier config always rejects one of them. Full guidance: Verifying tokens.

JWKS

Zynth Auth publishes its public signing keys as a JWKS document:

GET /.well-known/jwks.json
{
  "keys": [
    { "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "zynth-2026-q3", "n": "…", "e": "AQAB" }
  ]
}

Your services fetch and cache this to verify tokens offline — no per-request call to Zynth Auth. Match the token header's kid to the right key.

The document carries up to three keys, in this order:

SlotRole
previousRetiring — verifies only, never signs. Published until the last token it signed has expired.
activeThe key currently signing new tokens.
nextStaged ahead of a rotation — published before it ever signs, so caches have already seen it.

In steady state only the active key is published; previous and next appear around a rotation. That is what keeps verification unbroken across a cutover: tokens signed before it still verify against previous, and a verifier that refreshed early already trusts next.

Because the count varies, select by kid — don't assume a fixed number of keys, and don't treat keys[0] as the active one (during a rotation it is the retiring key).

Next