Skip to content
Guide

Keep your data in step with webhooks

Signed, retried events for users, sessions and organisations, verified and deduplicated by the SDK; back-channel logout for ended sessions.

You get
Your own database kept in step with users, sessions and organisations through verified, deduplicated events.
For
Backend developer
Time
About 20 min
Products
Sign-in
Organisations and roles

Add your endpoint (e.g. https://api.acme.example/hooks/auth) in the dashboard, Apps → Webhooks, and put its signing secret in ANCHRING_WEBHOOK_SECRET. Each environment's webhook has its own secret (promoting creates a new one for prod), so set the variable per environment.

api/hooks.ts
const auth = createAuth({ project: 'acme', env: process.env.AUTH_ENV ?? 'test', audience: 'acme-api' }); // reads ANCHRING_WEBHOOK_SECRET

app.post('/hooks/auth', auth.webhooks.handler({
  'user.created': (e) => createAccount(e.sub, e.email),
  'user.deleted': (e) => deleteAccount(e.sub),
}));

The handler checks the anchring-signature HMAC against the raw body with a 5 minute tolerance and deduplicates by event id: 401 for a bad or old signature, 200 for a replay or a type you don't handle, 500 if your handler throws (the event is retried). Deliveries are retried with backoff, so make handlers idempotent.

Events: user.created, user.updated, user.email_changed, user.deleted, user.disabled, user.enabled, session.revoked, credential.changed, invite.accepted, org.member_added, org.member_removed, org.member_updated, and for billing subscription.*, invoice.* and payment.*. An unknown event name in the handler is a type error.

Express

Signed bodies need the raw bytes: mount the handler before express.json().

import { toExpress } from '@anchring/auth/express';

app.post('/hooks/auth', toExpress(auth.webhooks.handler({ /* … */ }))); // before express.json()
app.use(express.json());

Rotating the secret

Rotate secret on the webhook returns the new secret once; the old one keeps signing next to it for a grace period (a day by default). Accept both meanwhile:

createAuth({ project: 'acme', env: 'prod', webhookSecret: [process.env.WEBHOOK_SECRET_NEW!, process.env.WEBHOOK_SECRET_OLD!] });

Webhook URLs must be https; plain http is accepted only for localhost in test. With several instances of your API, pass a shared replayStore so a replay to another instance is caught too.

Back-channel logout

When a user signs out, or an admin ends their sessions, Anchr.ing posts an OIDC back-channel logout token to the app's back-channel logout URI. Configure the app's clientId (the token's audience) in createAuth:

app.post('/auth/backchannel-logout', auth.backchannel.handler((e) => destroyAppSessions(e.sid, e.sub)));

// Reject app sessions the IdP has ended (by sid, or every session of a sub before the logout)
if (await auth.backchannel.isLoggedOut(c.var.user)) return c.json({ error: 'logged_out' }, 401);

Every option, error and token type: the SDK README.

Try it in your own test environment

Every feature works in test on the free plan, so the code above runs against your real sign-in.