Agent skill
Okta Webhooks Skill
Receive and verify Okta Event Hooks. Use when setting up Okta event hook handlers, implementing the one-time verification challenge, authenticating requests with the Authorization header secret, or handling identity events like user.lifecycle.create, user.session.start, user.account.lock, or group.user_membership.add.
Install this skill
npx skills add hookdeck/webhook-skills --skill okta-webhooks
When to Use This Skill
- Setting up Okta Event Hook handlers
- Implementing the one-time verification challenge (GET handshake)
- Authenticating Okta webhook requests with the
Authorizationheader secret - Understanding Okta event types and payloads
- Debugging why Okta event hook verification or delivery is failing
How Okta Event Hooks Differ
Okta Event Hooks do not use an HMAC signature. Security relies on two things:
- One-time verification handshake — When you register the hook, Okta sends a GET request with an
x-okta-verification-challengeheader. You must reply200with JSON{"verification": "<challenge value>"}. - Per-request authentication — You choose a secret string that Okta sends in the
Authorizationheader on every event delivery (an HTTPS POST). Verify it with a timing-safe comparison. There is no body signature.
Verification (core)
const crypto = require('crypto');
// 1. One-time verification handshake (GET)
function handleChallenge(req, res) {
const challenge = req.headers['x-okta-verification-challenge'];
return res.status(200).json({ verification: challenge });
}
// 2. Per-request auth on every event POST — timing-safe compare of Authorization
function isAuthorized(authHeader, secret) {
const a = Buffer.from(authHeader || '', 'utf8');
const b = Buffer.from(secret || '', 'utf8');
// Length check first: timingSafeEqual throws on unequal-length buffers
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python timing-safe compare: hmac.compare_digest(auth_header, secret).
For complete handlers with route wiring, event dispatch, and tests, see:
Common Event Types
Okta event hooks deliver System Log events. Each item in data.events[] has an eventType field:
| Event | Triggered When |
|---|---|
user.lifecycle.create | A new user is created |
user.lifecycle.activate | A user is activated |
user.session.start | A user signs in to Okta |
user.account.lock | A user account is locked |
user.account.unlock | A user account is unlocked |
group.user_membership.add | A user is added to a group |
group.user_membership.remove | A user is removed from a group |
For the full event catalog, see Okta event types.
Payload Structure
{
"eventType": "com.okta.event_hook",
"eventTime": "2026-07-02T12:00:00.000Z",
"eventId": "b5a4...",
"data": {
"events": [
{
"uuid": "d6f5...",
"eventType": "user.session.start",
"displayMessage": "User login to Okta",
"published": "2026-07-02T12:00:00.000Z",
"actor": { "id": "00u...", "type": "User", "alternateId": "jane@example.com" },
"target": [ { "id": "00u...", "type": "User", "alternateId": "jane@example.com" } ]
}
]
}
}
The outer eventType is always com.okta.event_hook. The System Log event type you dispatch on lives at data.events[].eventType.
Environment Variables
OKTA_WEBHOOK_SECRET=your-shared-secret # The Authorization header value you registered with Okta
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 okta --path /webhooks/okta
Reference Materials
- references/overview.md - Okta Event Hook concepts and events
- references/setup.md - Register an event hook in the Okta Admin Console
- references/verification.md - Verification challenge + Authorization auth details