huudis.session.revoked.v1

Fires when a session (or its associated refresh tokens) is revoked — whether by the user, by an admin, or by a cascading operation like password change.

Subscribe if you want to immediately invalidate any caches keyed on the session.

Not yet emitted in v1. Audit-log entries auth.logout, session_revoked, session.revoked_all, and oidc_consent_revoked carry equivalent data.

When it fires

  • Explicit sign-out at /v1/auth/logout.
  • User-initiated revoke at /v1/account/sessions/:id/revoke or /v1/account/sessions/revoke-all.
  • Admin-initiated revoke at /v1/ops/end-users/:id/revoke (cascades through every consent + refresh token for that user in the workspace's clients).
  • Password change at /v1/account/password-change — all sessions except the current one are revoked, generating one event per revoked session.
  • User disable (huudis.user.disabled.v1 fires alongside, plus a session.revoked per active session).
  • Consent revoke at /v1/account/connected-apps/:id.

Payload

{
  "id": "evt_01KPG…",
  "type": "huudis.session.revoked.v1",
  "createdAt": "2026-05-12T23:14:00.000Z",
  "data": {
    "sessionId": "sess_01KPG…",
    "userId": "usr_01KPG…",
    "userEmail": "adi@forjio.com",
    "clientId": "oc_plugipay_portal",
    "reason": "user_initiated",
    "revokedAt": "2026-05-12T23:14:00.000Z",
    "revokedBy": "usr_01KPG…"
  }
}

reason values:

Value Trigger
user_initiated User clicked sign-out or revoked from connected-apps.
admin_revoke Admin called the ops endpoint.
password_change Cascade from password change.
mfa_change Cascade from MFA enrolment or removal.
user_disabled Cascade from user disable.
expired Natural expiry. Reserved — not currently emitted; expired sessions are silently dropped.

revokedBy is null for natural expiry or password_change; otherwise the usr_… that initiated.

Handler examples

// Node — invalidate per-session caches
if (event.type === 'huudis.session.revoked.v1') {
  const s = event.data;
  await cache.invalidate(`session:${s.sessionId}`);
  if (s.reason === 'user_disabled') {
    await db.users.update(s.userId, { disabled: true });
  }
}
# Python
if event["type"] == "huudis.session.revoked.v1":
    s = event["data"]
    cache.invalidate(f"session:{s['sessionId']}")

What to do

  • Invalidate any access-token-keyed caches you maintain.
  • For user_disabled reason, treat as a stronger signal and revoke everything you have for the user (same as huudis.user.disabled.v1).
  • For admin_revoke, log it — this is a manual intervention that may be incident-related.

Avoid:

  • Notifying the user. They already initiated it (user_initiated) or were the target of an admin action (admin_revoke); a webhook-driven email would be noisy or alarming.

Common pitfalls

  • Bulk revoke storms. Calling revoke-all fires one event per revoked session — can be dozens. Your endpoint should handle bursts; retries pile up if you 5xx.
  • Order vs. session.created. Deliveries aren't strictly ordered — revoke can arrive before the corresponding created event in pathological retry scenarios. Idempotent-upsert by sessionId and compare timestamps if you need a happens-before.

Next