Skip to main content

Protect an API

An API must validate every bearer token before it trusts claims or performs protected work. Guardhouse SDKs support local JWT signature validation and remote introspection.

Choose a Validation Mode

ModeHow it worksPrefer it when
JWT signatureThe API validates the token locally with Guardhouse signing keysLow latency and availability without a validation request per call are the priority
IntrospectionThe API asks /connect/introspect whether the token is activeImmediate server-side token state and centralized validation are more important than an extra network dependency

Local JWT validation remains valid until token expiration unless the API applies another revocation strategy. Introspection requires confidential client credentials and makes Guardhouse availability part of the request path. Choose deliberately for each API.

Required Checks

Whichever mode you choose, require:

  • the canonical tenant issuer
  • the audience registered for this API resource
  • a valid token lifetime
  • a valid signature or active introspection result
  • the scope, role, permission, or policy needed by the operation

Decoding a JWT payload is not validation. Never trust claims read from an unverified token.

ASP.NET Core

Use the official Guardhouse.SDK package for local validation:

using Guardhouse.SDK.Extensions;
using Guardhouse.SDK.Models;

builder.Services.AddGuardhouseResource(options =>
{
options.Authority = "https://your-tenant.guardhouse.cloud";
options.Audience = "orders_api";
options.ValidationMode = TokenValidationMode.JwtSignature;
});

builder.Services.AddAuthorization();

Then place authentication before authorization in the pipeline and protect the endpoint with the Guardhouse scheme:

app.UseAuthentication();
app.UseAuthorization();

app.MapGet(
"/api/orders",
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Guardhouse")]
() => Results.Ok());

For introspection:

builder.Services.AddGuardhouseResource(options =>
{
options.Authority = "https://your-tenant.guardhouse.cloud";
options.Audience = "orders_api";
options.ValidationMode = TokenValidationMode.Introspection;
options.IntrospectionClientId = "YOUR_INTROSPECTION_CLIENT_ID";
options.IntrospectionClientSecret = "YOUR_INTROSPECTION_CLIENT_SECRET";
options.IntrospectionCredentialTransmission =
IntrospectionCredentialTransmission.FormData;
});

Store introspection credentials outside source control. See the .NET quickstart and .NET SDK reference.

Express

Use @guardhouse/node for local JWT validation and optional scope enforcement:

import { guardhouseMiddleware } from "@guardhouse/node";

app.use(
"/api/orders",
guardhouseMiddleware({
authority: "https://your-tenant.guardhouse.cloud",
audience: "orders_api",
requiredScopes: ["orders.read"],
}),
);

Switch the same middleware to introspection when required:

guardhouseMiddleware({
authority: "https://your-tenant.guardhouse.cloud",
audience: "orders_api",
validationMode: "introspection",
introspectionClientId: process.env.GUARDHOUSE_INTROSPECTION_CLIENT_ID,
introspectionClientSecret: process.env.GUARDHOUSE_INTROSPECTION_CLIENT_SECRET,
});

See the Node.js quickstart.

Authorization and Responses

Authentication middleware establishes a verified principal. Apply authorization at every protected endpoint or route group:

  • Return 401 Unauthorized for a missing, malformed, expired, or otherwise invalid token.
  • Return 403 Forbidden when the token is valid but lacks required access.
  • Do not rely on a hidden button or protected frontend route to secure a backend operation.
  • Keep CORS configuration separate from authentication and authorization.

Verification Checklist

Test the API with:

  • no Authorization header
  • a malformed bearer token
  • an expired token
  • a valid token from the wrong issuer
  • a valid token for the wrong audience
  • a valid token missing the required scope or policy
  • a valid token with the required access

Only the final case should reach the protected operation.