---
title: "Protect an Express App"
url: "https://docs.caracal.run/guides/protect-express/"
markdown_url: "https://docs.caracal.run/markdown/guides/protect-express.md"
description: "Add the caracalAuth middleware to Express routes to verify mandates and enforce scope requirements."
page_type: "page"
concepts: []
requires: []
---

# Protect an Express App

Canonical URL: https://docs.caracal.run/guides/protect-express/
Markdown URL: https://docs.caracal.run/markdown/guides/protect-express.md
Description: Add the caracalAuth middleware to Express routes to verify mandates and enforce scope requirements.
Page type: page
Concepts: none
Requires: none

---

Use `@caracalai/mcp-express` when an Express app should verify Caracal mandates before route handlers run.

## Install

```bash
npm install express @caracalai/mcp-express @caracalai/transport-mcp @caracalai/revocation
```

Use a Redis-backed revocation store in production. The in-memory store is only suitable for local development and tests.

## Add middleware

```ts
import express from "express";
import { caracalAuth, type CaracalRequest } from "@caracalai/mcp-express";
import { createMandateVerifier } from "@caracalai/transport-mcp";
import { InMemoryRevocationStore } from "@caracalai/revocation";

const app = express();

const verifier = createMandateVerifier({
  issuer: process.env.CARACAL_ISSUER!,
  audience: process.env.CARACAL_AUDIENCE!,
  zoneId: process.env.CARACAL_ZONE_ID!,
  revocations: new InMemoryRevocationStore(),
});

await verifier.warmup();

app.use("/mcp", caracalAuth({ verifier }, {
  requiredScopes: ["mcp:tool:call"],
  requiredTargets: ["https://mcp.example.com"],
}));

app.post("/mcp/tools/list", (req: CaracalRequest, res) => {
  res.json({
    principal: req.caracalClaims?.sub,
    tools: ["search", "summarize"],
  });
});
```

The middleware attaches verified claims to `req.caracal` and `req.caracalClaims`, plus the propagation context at `req.caracalContext`.

## Enforce the right boundary

| Option | Use it for |
| --- | --- |
| `requiredScopes` | Tool or route-level scope checks. |
| `requiredTargets` | Resource-target checks. |
| `requireAgent` | Reject non-agent mandates. |
| `requireDelegation` | Require delegated authority. |
| `maxHopCount` | Limit delegation depth. |

## Validate

1. Exchange for a mandate that targets the resource.
2. Call the protected route with `Authorization: Bearer <mandate>`.
3. Remove a required scope and confirm the route returns `403`.
4. Revoke the session and confirm the route rejects the old mandate.

Related pages: [Mandates](/concepts/mandate/) and [Sessions and Revocation](/concepts/sessions-revocation/).
