Server Auth
Verify Wacht session tokens and enforce permissions in backend JavaScript runtimes.
@wacht/backend verifies the session bearer token a Wacht frontend sends, turns it into a WachtAuth object, and lets you gate handlers on organization or workspace permissions. Use these helpers in any runtime with global fetch — Express, Bun, Workers, Deno, serverless. Token signatures are checked against your deployment's JWKS, which the SDK derives from your publishable key.
Core helpers
getAuth(request, options?)— read theAuthorization: Bearerheader off aRequestand return aWachtAuth.getAuthFromToken(token, options?)— same, when you already extracted the raw token.authenticateRequest(request, options?)—getAuthplus a serializedx-wacht-authheader for forwarding to downstream handlers.verifyAuthToken(token, options?)— verify a token and return the raw JWT payload, ornull.authFromHeaders(headers)— rebuildWachtAuthfrom a forwardedx-wacht-authheader without re-verifying.
None of these throw on an unauthenticated request. They return a WachtAuth with userId === null; the failure surfaces only when you call auth.protect(), which throws WachtAuthError.
Basic request auth
authenticateRequest returns { auth, headers } — destructure it. The headers carry the verified identity as x-wacht-auth for handing off to a downstream service; ignore them if you handle the request inline. auth.protect({ permission }) throws WachtAuthError with status 401 (no valid token) or 403 (token present but missing the permission).
import { authenticateRequest, WachtAuthError } from '@wacht/backend';
export async function handler(request: Request) {
try {
const { auth } = await authenticateRequest(request, {
signInUrl: 'https://app.example.com/sign-in',
});
await auth.protect({ permission: 'user:read' });
return new Response(JSON.stringify({ userId: auth.userId }), {
headers: { 'content-type': 'application/json' },
});
} catch (error) {
if (error instanceof WachtAuthError) {
return new Response(error.message, { status: error.status });
}
throw error;
}
}auth.has({ permission }) is the non-throwing variant — it returns a boolean and never redirects, so use it for conditional UI logic rather than route guards.
Token-only verification
Use verifyAuthToken when you only need the claims and want to make the authorization decision yourself. It returns null for any verification failure — bad signature, wrong issuer, expired, malformed — so treat null as "reject" without trying to distinguish why.
import { verifyAuthToken } from '@wacht/backend';
const payload = await verifyAuthToken(token);
if (!payload) {
throw new Error('Invalid token');
}
// payload.sub is the user id; payload.organization / payload.workspace carry scope.Publishable key resolution
Token verification needs the frontend host, which the SDK decodes from your publishable key. In Node.js it reads the key from the environment automatically:
WACHT_PUBLISHABLE_KEYNEXT_PUBLIC_WACHT_PUBLISHABLE_KEY
In runtimes without process.env (Workers, Deno with bindings), pass publishableKey explicitly in options; otherwise verification throws "Unable to derive frontend API URL from publishable key."
const { auth } = await authenticateRequest(request, {
publishableKey: env.WACHT_PUBLISHABLE_KEY,
});Machine credentials
getAuth and friends only verify Wacht session tokens. For API keys and OAuth access tokens, use the gateway API group (gateway.checkPrincipalAuthz), which checks revocation, scopes, and rate limits in one call. See the runtime guides for the full pattern.