Webhooks

Block-Auth calls your backend when something changes outside your app: a user closes their session or unlinks their account from Block-Auth Identity. Use it to end your own session or remove the link.

Set up the URL

In the Dashboard, open your app and fill in the third URL field. It is labeled When user fails to log in, but it is the webhook URL. See Webhook URL.

  • Write a path, such as /auth/blockauth/webhook, or a full URL. A path is sent to https:// plus your app's domain.
  • The URL must use https and be on your app's validated domain. Any other host is ignored.
  • Webhooks only go to your app's own domain, not to the automatic development domain (*.block-auth.io). To test them, use an app whose domain points to a server you control.
  • Don't use /signout/callback: it's the default value and means "no webhook".

Events

eventWhenWhat to do
signoutThe user closes their session for your app from Identity or the widget, or your backend calls /api/auth/signout and the user had sessions on other devices.End that user's session in your app.
account.unlinkedThe user removes the link between their Block-Auth identity and their account in your app, from Identity.Delete the link for externalId. The next login with Block-Auth asks to link again.

When your backend unlinks an account with /api/auth/link/revoke, no webhook is sent: you already know.

The request

A POST with a JSON body and two headers:

HeaderValue
X-BlockAuth-TimestampUnix time in seconds when it was sent.
X-BlockAuth-Signaturev1= followed by the signature in hex.

Body

{
  "event": "signout",
  "address": "0x7f85…46d8",
  "externalId": "user-1234",
  "sessionId": "8f1c…",
  "reason": "signed_out",
  "apiKey": "your_api_key",
  "iat": 1762851600,
  "jti": "3b9e0f4c2a…"
}
  • address is always lowercase.
  • externalId is your own user ID if the user is linked, or null.
  • sessionId is null when the signout comes from your own /api/auth/signout call.
  • jti is unique per request: use it to ignore a request you have already processed.

Verify the signature

Never trust a webhook you haven't verified. The signature is an HMAC-SHA256 of timestamp.body (the timestamp header, a dot and the raw body), keyed with the SHA-256 hash of your apiSecret.

  1. Read the raw body, before parsing JSON.
  2. Reject it if the timestamp is more than 5 minutes away from your clock.
  3. Compute the signature and compare it with X-BlockAuth-Signature in constant time.
  4. Check that apiKey is yours.

With the Block-Auth package routes you don't write any of this: POST /webhook does the four steps, and its isReplay and isRevoked options handle repeated deliveries and closing sessions. If you write the route yourself, verifyWebhook in AuthCodeClient does steps 1 to 3 in every language. The .NET SDK also checks apiKey; in Node.js, PHP and Python, check it yourself:

How you integrate it

const event = blockauth.verifyWebhook(
  req.body,
  req.get('X-BlockAuth-Timestamp'),
  req.get('X-BlockAuth-Signature'),
)
if (event.apiKey !== process.env.BLOCKAUTH_API_KEY) return res.status(400).end()

The full endpoint for each language is in the guides: React, Vue, Angular and PHP, Python, .NET, Node.js.

Delivery

  • Block-Auth sends each event once. There are no retries.
  • Your endpoint has 5 seconds to answer. Redirects are not followed.
  • Answer 2xx as soon as the signature is valid, and do the work after.

Because a webhook can be lost if your server is down, don't rely on it alone: your session should also end at signedExpiredAt.