Skip to content

authorization

Parses the Authorization request header and delegates credential verification to one or more pluggable strategies. Each strategy defines a scheme type, optional attribute matching, and an async authorizer function.

Pipeline stage: Authorization

import { authorization } from "@centralping/ergo";
OptionTypeDefaultDescription
strategiesArray<Strategy>[]Authentication strategy definitions
PropertyTypeRequiredDescription
typestringYesScheme name (Bearer, Basic, ApiKey, etc.)
attributesobjectNoAdditional matching criteria
authorizerfunctionYesAsync function — signature varies by scheme (see below)

The library parses credentials internally and calls your authorizer with a scheme-specific signature:

SchemeSignatureDescription
Basic(attributes, username, password) => { authorized, info }Base64 decoded and split on : by the library
Bearer(attributes, token) => { authorized, info }Raw token string (no decoding)
Custom(attributes, credentials) => { authorized, info }Raw credential string for any other scheme

On success, the middleware returns the info object from the matching strategy, stored at acc.auth:

{ user: { id: "42", role: "admin" } }

The shape of acc.auth is opaque — it is entirely determined by the info value your authorizer function returns when authorized: true. The TypeScript type is Record<string, unknown>; consumers narrow the type via application-level assertions or generics.

Multi-scheme examples:

// Bearer — authorizer returns decoded JWT claims
acc.auth // → { user: { id: "42", role: "admin" } }
// Basic — authorizer returns validated credentials
acc.auth // → { username: "jane", permissions: ["read", "write"] }
// API Key — authorizer returns service identity
acc.auth // → { service: "billing", scopes: ["invoices:read"] }

Because the middleware stores whatever your authorizer provides, you have full control over the shape. Design your info object to carry exactly what downstream handlers need — no more, no less.

StatusCondition
401 UnauthorizedMissing or invalid credentials (Bearer default; missing header with configured strategies)
403 ForbiddenDefault when authorized is false (Basic always; custom schemes; no configured strategies)
400 Bad RequestBearer only — authorizer returns info.type: 'invalid_request'

The exact status code depends on the scheme. See Per-Scheme Rejection Behavior below for the full mapping.

When a strategy provides an authenticate value, ergo includes a WWW-Authenticate response header with the challenge.

The status code and WWW-Authenticate challenge on authorization failure vary by scheme:

When the authorizer returns { authorized: false, info }, ergo maps the info.type field to an HTTP status code per RFC 6750 §3.1:

info.typeStatusDescription
'invalid_request'400 Bad RequestMalformed request (e.g. missing required parameter)
'insufficient_scope'403 ForbiddenValid token but insufficient privileges
(default)401 UnauthorizedInvalid or expired token ('invalid_token' or unset)

The response includes a WWW-Authenticate: Bearer header with error, error_description, and error_uri attributes derived from info.type, info.desc, and info.uri respectively.

When the authorizer returns { authorized: false }, ergo always responds with 403 Forbidden regardless of any statusCode or info properties. No WWW-Authenticate header is included in the rejection response.

When the Authorization header is missing, empty, or does not match any configured strategy:

  • With configured strategies: 401 Unauthorized with a combined WWW-Authenticate header listing all configured scheme challenges
  • With no configured strategies: 403 Forbidden
import { compose, authorization } from "@centralping/ergo";
const pipeline = compose(
[authorization({
strategies: [{
type: "Bearer",
authorizer: async (attributes, token) => {
const user = await verifyJwt(token);
return user
? { authorized: true, info: { user } }
: { authorized: false };
},
}],
}), "auth"],
);
import { compose, authorization } from "@centralping/ergo";
const pipeline = compose(
[authorization({
strategies: [{
type: "Basic",
attributes: { realm: "api" },
authorizer: async (attributes, username, password) => {
const user = await validateCredentials(username, password);
return user
? { authorized: true, info: { user } }
: { authorized: false };
},
}],
}), "auth"],
);

See the auto-generated authorization API docs.