AUTH4D-163 Flows guides and call_backend verification helpers
AUTH4D-163 adds the public Flows guides and server SDK helpers for call_backend endpoints. No
route, contract or migration changes.
Guides
| Page | Covers |
|---|---|
/docs/flows/ |
Triggers; application, project and built-in default resolution; drafts, versions, test runs. |
/docs/flows-steps/ |
Each built-in step, denial messages, and call_backend requests, answers and verification. |
/docs/flows-recipes/ |
Block banned Steam IDs, create a player profile on first sign-in, add a tier claim. |
/docs/limitations/#flows |
What Flows don’t do yet. |
The recipe definitions are examples/flows/*.json; the guides render them from the repository.
The call_backend example endpoint is examples/server/shared/flow-step.ts.
Server SDK: @auth4/sdk-server
packages/sdk-server/src/flows.ts, exported from the package root. It uses WebCrypto only, so it
runs on Workers, Node.js 22, Deno and Bun.
| Export | Does |
|---|---|
verifyFlowStepRequest(request, options) |
Reads a Fetch API Request body as text (at most 16 KiB) and verifies it. |
verifyFlowStepSignature(rawBody, headers, options) |
Verifies a raw body with Headers or a plain header object (for example Node’s IncomingHttpHeaders). |
flowStepResponse(answer) |
200 JSON, no-store, after checking answer against FlowBackendResponseSchema; throws TypeError for an answer the step refuses. |
flowStepRejectedResponse() |
401 { "error": "invalid_signature" }, with no reason. |
FlowStepVerificationError |
Thrown on a bad request; code is below. |
options is { secret, toleranceSeconds?, now? }. secret is the project’s whsec_… flows
signing secret (FlowSigningSecretSchema); any other value throws TypeError. toleranceSeconds
defaults to 300 and is capped at 3600, and applies both ways.
Verification follows the AUTH4D-160 signing rules: the HMAC-SHA256 of
{webhook-id}.{webhook-timestamp}.{raw body} keyed with the base64 part of the secret, sent as
v1,<base64>. Any one of several space-separated v1 signatures may match; other versions are
ignored. The comparison is crypto.subtle.verify. Only after the signature matches is the body
parsed and checked against FlowBackendEventSchema. The result is { id, timestamp, event }.
code |
When |
|---|---|
missing_headers |
webhook-id, webhook-timestamp or webhook-signature is missing. |
stale_timestamp |
The timestamp is not Unix seconds, or is outside the tolerance. |
invalid_signature |
No v1 signature matches. |
invalid_body |
The body is over 16 KiB, is not JSON, or is not a flow.step event. |
The helpers do not store webhook-ids. A backend that must refuse repeated requests stores them
for the tolerance window.
Tests
tests/integration/auth4d-163.flow-step-verify.test.ts drives the example endpoint through the
API’s real call_backend handler (deny, continue with claims, before registration, wrong secret),
checks each verification failure, flowStepResponse validation, and that every recipe definition
parses and has no publish problems for its documented triggers.
Source: docs/api-spec/auth4d-163.md