# FusionAuth Webhooks

## When to Use This Skill

* Setting up FusionAuth webhook handlers
* Debugging JWT signature verification failures
* Understanding FusionAuth event types and payloads
* Handling user, login, registration, or group events

## Essential Code (USE THIS)

### FusionAuth Signature Verification (JavaScript)

FusionAuth signs webhooks with a JWT in the `X-FusionAuth-Signature-JWT` header. The JWT contains a `request_body_sha256` claim with the SHA-256 hash of the request body.

```javascript
const crypto = require('crypto');
const jose = require('jose');

// Verify FusionAuth webhook signature
async function verifyFusionAuthWebhook(rawBody, signatureJwt, hmacSecret) {
  if (!signatureJwt || !hmacSecret) return false;

  try {
    // Create key from HMAC secret
    const key = new TextEncoder().encode(hmacSecret);

    // Verify JWT signature and decode
    const { payload } = await jose.jwtVerify(signatureJwt, key, {
      algorithms: ['HS256', 'HS384', 'HS512']
    });

    // Calculate SHA-256 hash of request body
    const bodyHash = crypto
      .createHash('sha256')
      .update(rawBody)
      .digest('base64');

    // Compare hash from JWT claim with calculated hash
    return payload.request_body_sha256 === bodyHash;
  } catch (err) {
    console.error('JWT verification failed:', err.message);
    return false;
  }
}

```

### Express Webhook Handler

```javascript
const express = require('express');
const crypto = require('crypto');
const jose = require('jose');

const app = express();

// CRITICAL: Use express.raw() - FusionAuth needs raw body for signature verification
app.post('/webhooks/fusionauth',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const signatureJwt = req.headers['x-fusionauth-signature-jwt'];

    // Verify signature
    const isValid = await verifyFusionAuthWebhook(
      req.body,
      signatureJwt,
      process.env.FUSIONAUTH_WEBHOOK_SECRET  // HMAC signing key from FusionAuth
    );

    if (!isValid) {
      console.error('FusionAuth signature verification failed');
      return res.status(401).send('Invalid signature');
    }

    // Parse payload after verification
    const event = JSON.parse(req.body.toString());

    console.log(`Received event: ${event.event.type}`);

    // Handle by event type
    switch (event.event.type) {
      case 'user.create':
        console.log('User created:', event.event.user?.id);
        break;
      case 'user.update':
        console.log('User updated:', event.event.user?.id);
        break;
      case 'user.login.success':
        console.log('User logged in:', event.event.user?.id);
        break;
      case 'user.registration.create':
        console.log('User registered:', event.event.user?.id);
        break;
      default:
        console.log('Unhandled event:', event.event.type);
    }

    res.json({ received: true });
  }
);

```

### Python (FastAPI) Webhook Handler

```python
import os
import hashlib
import base64
from fastapi import FastAPI, Request, HTTPException
import jwt

webhook_secret = os.environ.get("FUSIONAUTH_WEBHOOK_SECRET")

def verify_fusionauth_webhook(raw_body: bytes, signature_jwt: str, secret: str) -> bool:
    if not signature_jwt or not secret:
        return False

    try:
        # Verify and decode JWT
        payload = jwt.decode(signature_jwt, secret, algorithms=['HS256', 'HS384', 'HS512'])

        # Calculate SHA-256 hash of request body
        body_hash = base64.b64encode(hashlib.sha256(raw_body).digest()).decode()

        # Compare hash from JWT claim with calculated hash
        return payload.get('request_body_sha256') == body_hash
    except jwt.InvalidTokenError as e:
        print(f"JWT verification failed: {e}")
        return False

@app.post("/webhooks/fusionauth")
async def fusionauth_webhook(request: Request):
    payload = await request.body()
    signature_jwt = request.headers.get("x-fusionauth-signature-jwt")

    if not verify_fusionauth_webhook(payload, signature_jwt, webhook_secret):
        raise HTTPException(status_code=401, detail="Invalid signature")

    # Handle event...
    return {"received": True}

```

> For complete working examples with tests, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fusionauth-webhooks/examples/express/) - Full Express implementation
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fusionauth-webhooks/examples/nextjs/) - Next.js App Router implementation
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/fusionauth-webhooks/examples/fastapi/) - Python FastAPI implementation

## Common Event Types

| Event | Description |
| --- | --- |
| `user.create` | New user account created |
| `user.update` | User profile updated |
| `user.delete` | User account deleted |
| `user.deactivate` | User account deactivated |
| `user.reactivate` | User account reactivated |
| `user.login.success` | User successfully logged in |
| `user.login.failed` | User login attempt failed |
| `user.registration.create` | User registered for an application |
| `user.registration.update` | User registration updated |
| `user.registration.delete` | User registration deleted |
| `user.email.verified` | User email address verified |

> For full event reference, see [FusionAuth Webhook Events](https://fusionauth.io/docs/extend/events-and-webhooks/events/)

## Important Headers

| Header | Description |
| --- | --- |
| `X-FusionAuth-Signature-JWT` | JWT containing `request_body_sha256` claim |

## Environment Variables

```bash
FUSIONAUTH_WEBHOOK_SECRET=your_hmac_signing_key   # HMAC key from FusionAuth Key Master

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 fusionauth --path /webhooks/fusionauth

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fusionauth-webhooks/references/overview.md) - FusionAuth webhook concepts
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fusionauth-webhooks/references/setup.md) - Configuration guide
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/fusionauth-webhooks/references/verification.md) - Signature verification details