Skip to content

guides

Intended Documentation

Enforcement SDK

Use the Intended Enforcement SDK to enforce authorization decisions in your application code with middleware integration, automatic verification, and structured error handling.

Enforcement SDK#

The Intended Enforcement SDK provides a programmatic layer for enforcing authorization decisions in your application. Instead of manually verifying tokens and checking decisions, the SDK handles verification, claim inspection, expiry checks, and error propagation as middleware in your application stack.

Installation#

bash
npm install @intended/enforcement

Quick Setup#

Initialize the client

Create an enforcement client with your API key and JWKS endpoint. The client caches public keys and handles rotation automatically.

typescript
import { IntendedEnforcement } from "@intended/enforcement";

const enforcement = new IntendedEnforcement({
  apiKey: process.env.Intended_API_KEY!,
  jwksUrl: "https://api.intended.so/tenants/tenant_acme_prod/authority-keys/public",
  issuer: "intended.so",
  clockToleranceSec: 30,
});

Enforce a decision

Pass a decision token to the enforce method. It verifies the token, checks claims, and raises on denial.

typescript
try {
  const result = await enforcement.enforce(decisionToken, {
    expectedIntent: "summarize-document",
    expectedResource: "doc:inv-2024-003",
  });

  console.log(`Allowed: ${result.decision}`);
  console.log(`Actor: ${result.actor}`);
  // Proceed with the AI action
} catch (error) {
  if (error instanceof enforcement.DeniedError) {
    console.error(`Denied: ${error.reason}`);
  } else if (error instanceof enforcement.TokenExpiredError) {
    console.error("Token expired, re-evaluate the intent");
  }
}

Middleware Integration#

The SDK provides middleware adapters for common web frameworks. Middleware intercepts requests, extracts the decision token from the Authorization header, verifies it, and attaches the enforcement result to the request context.

Express.js Middleware#

typescript
import express from "express";
import { merittMiddleware } from "@intended/enforcement/express";

const app = express();

// Apply enforcement middleware globally
app.use(
  merittMiddleware({
    enforcement,
    tokenHeader: "X-Intended-Decision", // header carrying the decision token
    onDenied: (req, res, reason) => {
      res.status(403).json({
        error: "authorization_denied",
        reason,
      });
    },
  })
);

// Access enforcement result in route handlers
app.post("/api/summarize", (req, res) => {
  const { decision, actor, intent } = req.merittDecision;
  // decision is guaranteed "allow" if middleware passed
  res.json({ status: "processing", actor });
});

FastAPI Middleware#

python
from fastapi import FastAPI, Request
from meritt_enforcement.fastapi import IntendedMiddleware

app = FastAPI()

app.add_middleware(
    IntendedMiddleware,
    enforcement=enforcement,
    token_header="X-Intended-Decision",
)


@app.post("/api/summarize")
async def summarize(request: Request):
    decision = request.state.meritt_decision
    # decision.decision is guaranteed "allow" if middleware passed
    return {"status": "processing", "actor": decision.actor}

Selective Enforcement#

Apply enforcement only to specific routes:

typescript
import { merittGuard } from "@intended/enforcement/express";

const requireDecision = merittGuard(enforcement, {
  tokenHeader: "X-Intended-Decision",
});

// Only this route requires a valid decision token
app.post("/api/summarize", requireDecision, (req, res) => {
  res.json({ status: "processing" });
});

// This route does not require enforcement
app.get("/api/health", (req, res) => {
  res.json({ status: "ok" });
});

Configuration Reference#

OptionTypeDefaultDescription
apiKeystringrequiredIntended API key for fallback remote verification
jwksUrlstringrequiredURL to fetch public signing keys
issuerstring"intended.so"Expected token issuer
clockToleranceSecnumber30Seconds of clock skew tolerance
jwksCacheTtlSecnumber86400How long to cache JWKS keys (seconds)
fallbackToRemotebooleantrueFall back to remote /tokens/verify if local fails
strictModebooleanfalseReject tokens missing optional claims

Error Handling#

The SDK raises specific error types so you can handle each failure mode:

Error ClassCauseRecommended Action
DeniedErrorPolicy denied the intentLog and return 403 to the caller
TokenExpiredErrorDecision token TTL exceededRe-evaluate intent via /intent
TokenInvalidErrorSignature verification failedReject request; potential tampering
ClaimMismatchErrorToken claims do not match expectationsReject request; wrong token for this action
JwksUnavailableErrorCannot fetch JWKS keysIf fallbackToRemote is true, retries via API; otherwise fail open or closed per config
NetworkErrorConnectivity issueRetry with backoff; check network

Warning

By default, the SDK fails closed: if it cannot verify a token for any reason, the request is denied. Set failOpen: true only if you accept the risk of unverified decisions in degraded conditions.

Custom Error Handler#

typescript
const enforcement = new IntendedEnforcement({
  apiKey: process.env.Intended_API_KEY!,
  jwksUrl: "https://api.intended.so/tenants/tenant_acme_prod/authority-keys/public",
  onError: (error, context) => {
    // Send to your observability stack
    logger.error("Enforcement error", {
      error: error.message,
      intent: context.expectedIntent,
      resource: context.expectedResource,
    });

    // Return false to deny, true to allow (fail-open)
    return false;
  },
});

Logging and Observability#

The SDK emits structured events you can pipe to your logging infrastructure:

typescript
enforcement.on("decision:enforced", (event) => {
  metrics.increment("meritt.enforcement.allowed", {
    intent: event.intent,
    actor: event.actor,
  });
});

enforcement.on("decision:denied", (event) => {
  metrics.increment("meritt.enforcement.denied", {
    intent: event.intent,
    reason: event.reason,
  });
});

enforcement.on("verification:failed", (event) => {
  metrics.increment("meritt.enforcement.verification_failed", {
    error: event.errorType,
  });
});

Troubleshooting#

ProblemCauseRemediation
Middleware returns 403 on every requestToken header name mismatchVerify tokenHeader matches the header your client sends
JwksUnavailableError on startupNetwork or DNS issueEnsure the service can reach api.intended.so; check firewall rules
High latency on first requestJWKS cold cacheThe first request fetches keys; subsequent requests use cache
ClaimMismatchErrorToken issued for different intent/resourceEnsure the token matches the exact action being performed

Next Steps#