Agent skill
Cloudentity (SecureAuth) Webhooks Skill
Receive and authenticate Cloudentity (SecureAuth) webhooks. Use when setting up a Cloudentity or SecureAuth Connect webhook endpoint, checking the X-API-Key header, debugging 401s, or handling batched audit events like user.created, user.authenticated, group.created and client.created.
Install this skill
npx skills add hookdeck/webhook-skills --skill cloudentity-webhooks
Cloudentity is now SecureAuth (cloudentity.com redirects to secureauth.com); this skill keeps the cloudentity name. SecureAuth Connect workspace webhooks send batches of audit events as JSON ({ "audit_) when subscribed events happen, such as user.created or user.authenticated. Webhooks are not signed. Each request carries an X-API-Key header with the Webhook API Key, generated by SecureAuth and shown once when you create the webhook.
A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS, and rotate the key if it leaks.
When to Use This Skill
- How do I receive Cloudentity or SecureAuth webhooks?
- How do I verify the
X-API-Keyheader on a SecureAuth Connect webhook? - Does Cloudentity sign webhook payloads? (No: use the Webhook API Key)
- How do I handle
user.created,user.deletedorlogin.failedaudit events? - Why does my Cloudentity webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyCloudentityRequest(headers, apiKey = process.env.CLOUDENTITY_WEBHOOK_API_KEY) {
if (!apiKey) return false; // never accept when unconfigured
const received = headers['x-api-key']; // whole value, plain text
return Boolean(received) && safeEqual(received, apiKey);
}
- Header:
X-API-Key: <Webhook API Key>(case-insensitive name, whole value compared). - Python:
hmac.compare_on bytes, never ondigest(received.encode(), key.encode()) str. - Return
401on a missing or wrong key,400for a body without anaudit_eventsarray,200once handled. - Read the body raw (
express.raw({ type: '*/*' })) and authenticate before parsing it.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
Event identifiers use a subject.action form (user.deleted); the docs say the payload delivers the matching field in subject_action form (user_deleted). The docs don't name the field that carries the event type. The external-logging guide reads audit_, so the examples infer that each audit event's payload object holds one key named for the event and dispatch on that key. This is an inference, not documented: confirm it against a real delivery. Available events depend on the server type.
| Event | Sent When |
|---|---|
user.created | A user is created |
user.updated | A user is updated |
user.deleted | A user is deleted (also at workspace and organization level) |
user.authenticated | A user authenticates |
group.created | A group is created |
login.accepted / login.failed | A sign-in attempt succeeds / fails |
consent.created / consent.revoked | An Open Banking, CDR or FDX consent is created / revoked |
client.created | An application (client) is created |
Other documented events include access_token.issued, password.updated, otp.sent and organization.created. See references/overview.md.
Environment Variables
CLOUDENTITY_WEBHOOK_API_KEY=your-webhook-api-key # shown once when the webhook is created
Local Development
SecureAuth can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 cloudentity --path /webhooks/cloudentity
Use the printed URL as the webhook URL in SecureAuth.
Resources
- references/overview.md - What the webhooks are, events and batch payload shape
- references/setup.md - Create the webhook and copy the API key
- references/verification.md - Key check details, limits, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests