Documentation

Production Integration Guide

OAuth authorization code integration for ZKAuth, aligned with live backend behavior and written for engineering teams shipping secure production flows.

Estimated setup: 30-45 minutesFlow type: Authorization Code + PKCEToken TTL: 15 minutes

Flow architecture

Authorization path

Redirect users to ZKAuth, complete identity verification, and receive a single-use authorization code.

Token exchange path

Perform backend-only code exchange at the token endpoint with strict validation and credential protection.

Session binding path

Verify JWT signature and claims, bind `sub` to local user records, then issue your own app session.

Prerequisites

Provide these values before client onboarding:

  • Application Name (consent screen display)
  • Exact Redirect URI allowlist
  • Client type selection (confidential or public)

Provisioning output (admin-managed or self-service):

  • Client ID
  • Client Secret (currently required during token exchange)
  • Redirect URI allowlist bound to that client profile

Actor flow matrix

ActorRole
UserAuthenticates and approves consent
Client FrontendRedirects browser to authorization endpoint
ZKAuth IdentityPerforms identity checks and issues authorization code
Client BackendExchanges code for token and creates app session

Client onboarding modes

ZKAuth supports both enterprise admin-controlled provisioning and authenticated client-side self-service registration for application owners.

Admin-managed generation

Security/Admin teams can create and rotate OAuth clients centrally from admin tooling and distribute credentials to app teams.

Client self-service generation

Client operators can login to the client dashboard and register their own OAuth app profile under their authenticated identity scope.

Endpoint surface

Authorize

https://auth.zkauth.tech/api/oauth/authorize

Browser redirect endpoint (code flow)

Token

https://api.zkauth.tech/api/oauth/token

Server-to-server code exchange

JWKS

https://api.zkauth.tech/.well-known/jwks.json

Key discovery for JWT verification

Step-by-step implementation

01

Frontend redirects to /authorize

Send client_id, redirect_uri, response_type=code, state, and optional PKCE params. Public clients require PKCE.

02

User authenticates and confirms consent

ZKAuth Identity UI handles login, checks session/cookies, and confirms authorization intent before issuing code.

03

Backend validates callback state

Validate state exactly and extract the short-lived single-use authorization code.

04

Backend exchanges code at /token

POST grant_type=authorization_code, code, redirect_uri, client_id, client_secret, and code_verifier when PKCE is used.

05

Backend verifies token and binds local session

Verify RS256 signature using JWKS, validate iss/aud/exp, link subject to local user, and issue your own app session.

Authorization request contract

ParameterRequiredNotes
client_idYesIssued by ZKAuth admin
redirect_uriYesMust exactly match registered URI
response_typeYesMust be `code`
stateYesCSRF protection token from client
scopeOptionalRequested profile scope set
code_challengePublic: YesPKCE S256 challenge
code_challenge_methodWith PKCEUse `S256`

Token request/response

`POST /api/oauth/token` with JSON payload:

{
    "grant_type": "authorization_code",
    "code": "AUTH_CODE",
    "redirect_uri": "https://client-app.com/auth/callback/zkauth",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "code_verifier": "PKCE_VERIFIER_IF_USED"
}

Response:

{
    "access_token": "eyJhbGciOiJSUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 900
}

Authorize redirect

const authUrl = new URL('https://auth.zkauth.tech/api/oauth/authorize');
authUrl.searchParams.set('client_id', process.env.NEXT_PUBLIC_ZKAUTH_CLIENT_ID!);
authUrl.searchParams.set('redirect_uri', 'https://client-app.com/auth/callback/zkauth');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('state', state);
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
window.location.href = authUrl.toString();

Token exchange

const tokenResp = await axios.post('https://api.zkauth.tech/api/oauth/token', {
  grant_type: 'authorization_code',
  code,
  redirect_uri: 'https://client-app.com/auth/callback/zkauth',
  client_id: process.env.ZKAUTH_CLIENT_ID,
  client_secret: process.env.ZKAUTH_CLIENT_SECRET,
  code_verifier
});

const accessToken = tokenResp.data.access_token;

Claim validation

const claims = jwt.verify(accessToken, publicKey, {
  algorithms: ['RS256'],
  issuer: 'https://api.zkauth.tech',
  audience: process.env.ZKAUTH_CLIENT_ID
});

// Stable user anchor
const zkauthSub = claims.sub;

Secure rollout checklist

  • Register exact redirect URI values; avoid wildcards.
  • Always generate and validate high-entropy `state` values.
  • Enable PKCE with S256 in all clients (required for public clients).
  • Perform token exchange only from backend services.
  • Verify JWT with JWKS and validate `iss`, `aud`, `exp` claims.
  • Store only `sub` mapping as identity anchor in local DB.

Current implementation caveats

  • Current backend requires `client_secret` in token exchange for all clients.
  • Do not assume `username` or `email` exist in access token claims.
  • Do not depend on userinfo endpoint in production integration flow yet.
  • Treat refresh token support as unavailable until explicitly released.