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';
Option Type Default Description
strategies Array<Strategy> [] Authentication strategy definitions
Property Type Required Description
type string Yes Scheme name (Bearer, Basic, ApiKey, etc.)
attributes object No Additional matching criteria
authorizer function Yes Async function — signature varies by scheme (see below)

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

Scheme Signature Description
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 on the domain accumulator 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.

Status Condition
401 Unauthorized Missing or invalid credentials (Bearer default; missing header with configured strategies)
403 Forbidden Default when authorized is false (Basic always; custom schemes; no configured strategies)
400 Bad Request Bearer only — authorizer returns info.error: '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.error field to an HTTP status code per RFC 6750 §3.1:

info.error Status Description
'invalid_request' 400 Bad Request Malformed request (e.g. missing required parameter)
'insufficient_scope' 403 Forbidden Valid token but insufficient privileges
(default) 401 Unauthorized Invalid or expired token ('invalid_token' or unset)

The response includes a WWW-Authenticate: Bearer header and, when present on info, emits error, error_description, and error_uri challenge attributes from the same-named fields (omitted when unset).

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};
},
}],
}),
);
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};
},
}],
}),
);

See the auto-generated authorization API docs.