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 Authorization header 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:

  1. One-time verification handshake — When you register the hook, Okta sends a GET request with an x-okta-verification-challenge header. You must reply 200 with JSON {"verification": "<challenge value>"}.
  2. Per-request authentication — You choose a secret string that Okta sends in the Authorization header 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:

EventTriggered When
user.lifecycle.createA new user is created
user.lifecycle.activateA user is activated
user.session.startA user signs in to Okta
user.account.lockA user account is locked
user.account.unlockA user account is unlocked
group.user_membership.addA user is added to a group
group.user_membership.removeA 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Aug 1, 2026

View on GitHub →