Sign in

Protect API Routes

This guide shows how to protect Express API routes using protectApi() from the MonoCloud Backend Node SDK.

You can protect routes globally, per-route, or combine both approaches with scope and group-based authorization.

What you'll cover

  • Protect all routes globally
  • Protect individual routes
  • Require specific scopes
  • Require specific groups
  • Read claims from the authenticated token

Before you begin

This guide assumes you've completed the Express quickstart or the installation guide.

You should already have:

  • An Express project
  • The @monocloud/backend-node SDK installed
  • Environment variables configured in .env

Protect all routes globally

Apply protectApi() as application-level middleware to protect all routes.

src/server.ts
import "dotenv/config";
import express from "express";
import {
  protectApi,
  type AuthenticatedExpressRequest,
} from "@monocloud/backend-node/express";

const app = express();
const protect = protectApi();

app.use(express.json());
app.use(protect());

app.get("/api/data", (req, res) => {
  const { claims } = req as AuthenticatedExpressRequest;
  res.json({ claims });
});

app.listen(3000);

How it works:

  • protectApi() creates a middleware factory from environment variables
  • protect() returns Express middleware that validates the Bearer token
  • Every request must include a valid Authorization: Bearer <token> header
  • Validated claims are attached to req.claims

Protect individual routes

Apply the middleware to specific routes instead of globally.

src/server.ts
import "dotenv/config";
import express from "express";
import {
  protectApi,
  type AuthenticatedExpressRequest,
} from "@monocloud/backend-node/express";

const app = express();
const protect = protectApi();

app.use(express.json());

// Public route — no token required
app.get("/api/public", (req, res) => {
  res.json({ message: "Public data" });
});

// Protected route — requires a valid access token
app.get("/api/protected", protect(), (req, res) => {
  const { claims } = req as AuthenticatedExpressRequest;
  res.json({ claims });
});

app.listen(3000);

Require specific scopes

Pass scopes to protect() to require specific access token scopes.

ts
app.get("/api/admin", protect({ scopes: ["write"] }), (req, res) => {
  const { claims } = req as AuthenticatedExpressRequest;
  res.json({ claims });
});

Behavior:

  • Returns 401 Unauthorized if the token is missing or invalid
  • Returns 403 Forbidden if the token is valid but lacks the required scopes
  • Executes the handler if the token contains all required scopes

Require specific groups

Pass groups to protect() to require specific group memberships.

ts
app.get("/api/team", protect({ groups: ["engineering"] }), (req, res) => {
  const { claims } = req as AuthenticatedExpressRequest;
  res.json({ claims });
});

Behavior:

  • Returns 401 Unauthorized if the token is missing or invalid
  • Returns 403 Forbidden if the token is valid but the user is not in the required group
  • Executes the handler if the user belongs to any of the specified groups

Combine global and per-route protection

You can apply global token validation and add scope or group requirements on specific routes.

src/server.ts
import "dotenv/config";
import express from "express";
import {
  protectApi,
  type AuthenticatedExpressRequest,
} from "@monocloud/backend-node/express";

const app = express();
app.use(express.json());

const protect = protectApi();

// All routes require a valid access token
app.use(protect());

// Any authenticated user can access this route
app.get("/api/data", (req, res) => {
  const { claims } = req as AuthenticatedExpressRequest;
  res.json({ claims });
});

// Only users with the write scope and in the engineering group can access this route
app.get("/api/admin", protect({ scopes: ["write"], groups: ["engineering"] }), (req, res) => {
  const { claims } = req as AuthenticatedExpressRequest;
  res.json({ claims });
});

app.listen(3000);

Response behavior

ScenarioStatus codeResponse
Missing or invalid token401{ "message": "unauthorized" }
Valid token, missing scopes403{ "message": "forbidden" }
Valid token, missing groups403{ "message": "forbidden" }
Valid token, authorizedRoute handler executes normally
© 2024 MonoCloud. All rights reserved.