Skip to main content

Webhooks

Outbound webhook delivery for security events. Subscribers can be notified when users register, change passwords, get removed from orgs, etc.

The webhook system lives in identsphere-webhooks — a separate crate so hosts that don't need outbound webhooks aren't forced to depend on it.

Signing

Every webhook delivery carries:

POST {your_url}
X-IdentSphere-Signature: t=1748448000,v1=abc...hmac_sha256_hex
X-IdentSphere-Event: user.registered
Content-Type: application/json

{
"event": "user.registered",
"data": { ... }
}

Verify the signature in your handler:

import crypto from 'crypto';

function verify(payload: string, header: string, secret: string): boolean {
const [tPart, sigPart] = header.split(',');
const t = tPart.split('=')[1];
const v1 = sigPart.split('=')[1];

// Reject anything older than 5 minutes — protects against replay.
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;

const expected = crypto
.createHmac('sha256', secret)
.update(`${t}.${payload}`)
.digest('hex');

// constant-time compare
return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Same pattern as Stripe, GitHub, etc. — the timestamp prefix defeats replay.

Events

EventPayload
user.registered{ user_id, organization_id, email }
user.password_changed{ user_id }
member.role_changed{ user_id, organization_id, old_role, new_role }
member.removed{ user_id, organization_id }
mfa.enabled / mfa.disabled{ user_id }

Full list: see the Event enum in identsphere_webhooks::events.

Subscriptions

Endpoints are configured per-deployment, not per-user. Provide them at app startup:

use identsphere_webhooks::{WebhookDelivery, WebhookConfig};

let delivery = WebhookDelivery::new(WebhookConfig {
endpoints: vec![
("https://hooks.example.com/identsphere".into(), b"secret-here".to_vec()),
],
..Default::default()
});

Multiple endpoints fan out — each gets its own delivery + signature.

Retry policy

  • Initial attempt + 4 retries with exponential backoff (1s, 4s, 16s, 60s, 300s).
  • Successful responses are 2xx within 10s.
  • 5xx or timeout → retry; 4xx → drop.

The retry queue is in-process. For production durability, wrap WebhookDelivery in your own SQS / Kafka producer.

Local development

Use ngrok or webhook.site to receive deliveries from a localhost IdentSphere instance. The signature scheme is identical to production.

Limits

  • v0.1 webhooks are FIRE-AND-FORGET from the SDK's perspective — there's no "delivery succeeded" callback you can hook into.
  • No event filtering per-endpoint in v0.1 (all subscribers get all events). Filter on the receiver side.

Cross-product wiring

The webhooks crate works fine without identsphere-axum, so non-Rust receivers just need to verify the HMAC. The signing key is whatever bytes you configured at the producer; rotate at will.