Middleware Reference

Complete reference for all middleware exported by @riavzon/auth, covering authentication guards, security layers, verification handlers, and request enrichment.

Every middleware exported by @riavzon/auth is documented here. Middleware functions follow the standard Express (req, res, next) signature unless noted otherwise. When using bootstrapApp(), the global middleware stack is already wired in the correct order. You only need to apply route-level middleware when building custom routes.

All middleware depends on a resolved configuration. Call configuration() or bootstrapApp() before mounting any of these functions.

For the full middleware stack order applied by bootstrapApp(), see the Service page. For route-level middleware chains per endpoint, see the Routes Reference.


Authentication Guards

These middleware functions enforce token presence and validity on protected routes. They don't verify the token contents themselves -- that responsibility belongs to protectRoute.

requireAccessToken

Extracts the Bearer token from the Authorization header and attaches it to req.token. If the header is missing or malformed, the request is rejected immediately.

Import

import { requireAccessToken } from '@riavzon/auth'

Reads

SourceValue
Authorization headerMust start with Bearer followed by the token string

Sets

TargetValue
req.tokenThe raw access token string (header value after Bearer )

Responses on failure

StatusBody
401{ ok: false, error: 'Missing Bearer token' }
401{ error: 'Access token missing' } (empty token after prefix)
This middleware only checks that a token is present. It does not verify the signature or decode the payload. Use protectRoute downstream for full verification.

checkForActiveMfa

Checks the anomaly cache for the current refresh token and short-circuits requests that already have an unresolved MFA or re-login requirement. It hashes req.cookies.session, looks up that key in anomaliesCache(), and returns the cached response before the request reaches protectRoute or a sensitive controller.

Import

import { checkForActiveMfa } from '@riavzon/auth'

Reads

SourceValue
req.cookies.sessionThe refresh token cookie

Responses on failure

StatusBody
202{ mfa: true, message: 'A login link has been sent to your email.' }
401{ error: 'Re-login is required', message: cached.anomalyType }
If the session cookie is missing, or if the cached anomaly is already resolved, this middleware calls next() and leaves the request untouched.
The built-in BFF, API management, custom MFA, and refresh-session routes place this middleware after getFingerPrint and before protectRoute or the controller.

requireRefreshToken

Checks that the session cookie containing the refresh token is present. Rejects the request if the cookie is missing.

Import

import { requireRefreshToken } from '@riavzon/auth'

Responses on failure

StatusBody
401{ error: 'Re-login is required', message: cached.anomalyType }
202{ mfa: true, message: 'A login link has been sent to your email.' }

Reads

SourceValue
req.cookies.sessionThe refresh token cookie

Responses on failure

StatusBody
401{ error: 'Refresh token missing' }
This middleware does not consume or verify the refresh token. Downstream handlers like protectRoute or the rotation controllers handle the actual verification.

protectRoute

The primary authentication middleware. Verifies the access token signature, decodes the payload, runs the full anomaly detection pipeline against the session context, and enriches the request with the authenticated user's identity. If anomalies trigger MFA, the middleware sends the challenge email and responds with 202.

Import

import { protectRoute } from '@riavzon/auth'

Reads

SourceValue
req.tokenThe raw access token (set by requireAccessToken)
req.cookies.sessionThe refresh token cookie
req.cookies.canary_idThe canary cookie for anomaly detection
req.ipClient IP address
req.fingerPrintDevice fingerprint (set by getFingerPrint)
User-Agent headerBrowser and device identification

Sets

TargetValue
req.user.userIdThe authenticated user's numeric ID
req.user.visitor_idThe visitor identifier string
req.user.accessTokenIdThe access token's jti claim (useful for rate limiter keying)
req.user.rolesArray of role strings from the token payload
req.user.payloadThe full decoded JWT payload object

Responses on failure

StatusBodyMeaning
202MFA challenge email sentAnomaly detection triggered adaptive MFA
401{ error: '...' } or { error: '...', reason: '...' }Missing token, missing refresh cookies, malformed payload, verification failed, or re-login required (includes reason field with the anomaly description)
500Internal errorMFA dispatch failure or verification flow error

Anomaly detection

The middleware calls strangeThings() internally, which runs nine checks including cookie binding, IP drift, user-agent drift, and geo anomalies. When anomalies are detected but the session is recoverable, the middleware automatically sends an MFA challenge email to the user and responds with 202 Accepted. The client must then complete the MFA flow before retrying the original request.

See the full Anomaly Detection pipeline for details on each check.

Always place requireAccessToken and requireRefreshToken before protectRoute in the middleware chain. The protectRoute middleware expects req.token and the refresh cookies to already be present.

Recommended chain

router.get('/protected',
  requireAccessToken,
  requireRefreshToken,
  getFingerPrint,
  protectRoute,
  yourHandler
)

acceptCookieOnly

A strict input guard for cookie-only endpoints such as token rotation and logout. Rejects any request that carries a body, query string, or Content-Type header. This prevents injection vectors on endpoints that should only read from cookies.

Import

import { acceptCookieOnly } from '@riavzon/auth'

Reads

SourceValue
req.cookies.sessionMust be present (string)
req.bodyMust be empty or absent
content-length headerMust be 0 or absent
transfer-encoding headerMust not contain chunked
Query stringMust be empty
Content-Type headerMust be absent

Responses on failure

StatusBody
401{ error: 'Refresh token missing' }
400{ error: 'Request body not allowed' }
400{ error: 'Query string not allowed' }
400{ error: 'Content-Type not allowed' }
Place this after requireRefreshToken but before the controller. The middleware validates that only cookies are used for authentication data -- no body, no query parameters, no content type.

Security Middleware

These middleware functions are applied globally by bootstrapApp(). You typically don't need to add them manually unless you're building a custom Express app without bootstrapApp().

helmet

Applies the Helmet security headers to every response. Pre-configured with the service's security policy.

Headers set

HeaderValue
X-Frame-OptionsDENY
Content-Security-Policyframe-ancestors 'none'
Referrer-Policyorigin
Cross-Origin-Embedder-PolicyEnabled
Standard Helmet defaultsX-Content-Type-Options, X-DNS-Prefetch-Control, etc.
This middleware is not exported from @riavzon/auth. It is applied automatically by bootstrapApp().

headers

Sets standard no-cache response headers on every response. This prevents browsers and proxies from caching sensitive authentication responses.

Headers set

HeaderValue
Cache-Controlno-cache, private, max-age=0
Pragmano-cache
Expires0
This middleware is not exported from @riavzon/auth. It is applied automatically by bootstrapApp().

validateIp

Validates that req.ip resolves to a valid IP address using Node's net.isIP(). If the IP is missing or invalid, the request is rejected with 403 Forbidden.

Responses on failure

StatusBody
403Forbidden (plain text)
This middleware is not exported from @riavzon/auth. It is applied automatically by bootstrapApp().

hmacAuth

Verifies inbound requests using HMAC headers and a shared secret. Only active when service.Hmac is configured. Validates the client identity, timestamp freshness, signature integrity, and request uniqueness (replay protection via nonce cache).

Required headers

HeaderDescription
X-Client-IdThe client identifier matching the configured clientId
X-TimestampClient timestamp in milliseconds
X-SignatureHMAC-SHA256 hex digest of clientId:timestamp:method:url:requestId
X-Request-IDA unique request identifier (nonce)

Validation steps

  1. Checks that all four headers are present
  2. Verifies the X-Client-Id matches the configured value
  3. Rejects requests with clock skew exceeding the configured threshold (default: 5 minutes)
  4. Rejects replayed request IDs using an LRU nonce cache
  5. Recomputes the HMAC-SHA256 signature and compares using crypto.timingSafeEqual()

Bypass

  • Local GET /health requests (from 127.0.0.1 or ::1) are allowed without HMAC headers

Responses on failure

StatusBody
401Reason string (missing headers, unknown client, timestamp skew, replay detected, or signature mismatch)

See the HMAC Authentication page for integration details and client-side signing examples.

This middleware is not exported from @riavzon/auth. It is applied automatically by bootstrapApp() when service.Hmac is configured.

validateContentType

A factory function that returns middleware enforcing a specific Content-Type header. If the request's content type does not match the expected value, the request is rejected with 403 Forbidden.

Import

import { validateContentType } from '@riavzon/auth'

Signature

function validateContentType(expected: string): RequestHandler

Parameters

expected
string required
The expected MIME type, for example 'application/json'.

Usage

router.post('/data',
  validateContentType('application/json'),
  express.json(),
  yourHandler
)

Responses on failure

StatusBody
403{ error: 'not allowed.' }

Verification Middleware

These middleware functions handle magic link and MFA verification on specific routes. They are used in the magic link route chains and are also exported for custom route composition.

verifyMFA

Verifies a one-time MFA code submitted via email. On success, issues new access and refresh tokens and calls next(). On failure, responds with the appropriate error status.

Import

import { verifyMFA } from '@riavzon/auth'

Reads

SourceValue
req.body.codeThe MFA code submitted by the user
req.link.purposeMust be MAGIC_LINK_MFA_CHECKS (set by upstream link verification)
req.link.subjectMust match MAGIC_LINK_MFA_CHECKS_{visitor}
req.link.visitorThe visitor ID from the verified magic link

Responses

StatusMeaning
200Code verified, new tokens issued
400Malformed input or purpose/subject mismatch
401Invalid or expired code
403User is banned
500Internal error

See MFA for the full adaptive MFA flow.


verifyNewPassword

Handles the password reset completion step after a user clicks the magic link. Validates the new password against the Zod schema, checks for data breach exposure, hashes the password, and updates it in the database.

Import

import { verifyNewPassword } from '@riavzon/auth'

Reads

SourceValue
req.body.passwordThe new password
req.body.confirmedPasswordPassword confirmation (must match)
req.link.purposeMust be PASSWORD_RESET (set by upstream link verification)
req.link.subjectMust match PASSWORD_RESET_{visitor}
req.link.jtiThe link's unique identifier for rate limiting

Responses

StatusMeaning
200Password updated
400Validation error, password mismatch, or password found in data breaches
403XSS attempt detected (user banned)

Rate limiting

This middleware applies per-IP and per-composite-key (ip_visitor) rate limiting with consecutive failure tracking. See Rate Limiting for details.


customMfaFlowsVerification

Verifies custom MFA magic links used for sensitive actions such as account deletion, payment confirmation, or email changes. Validates the temporary JWT token, checks the random hash using crypto.timingSafeEqual(), enforces single-use semantics, and populates req.link on success.

Import

import { customMfaFlowsVerification } from '@riavzon/auth'

Reads

SourceValue
req.query.tokenThe temporary JWT token from the magic link URL
req.query.randomThe random hash for cryptographic verification
req.query.reasonThe MFA flow reason (for example, PAYMENT_CONFIRM)
req.query.visitorThe visitor identifier

Sets

TargetValue
req.link.visitorThe verified visitor ID
req.link.subjectThe link subject string
req.link.purposeThe link purpose/reason
req.link.jtiThe link's unique identifier

Behavior by HTTP method

MethodBehavior
GETPreview the link status. Limited to a configurable number of previews. Returns 200 with link metadata.
POSTConsume the link. Limited to a single use. On success, populates req.link and calls next().

Responses on failure

StatusMeaning
400Invalid, expired, or already-used link; validation errors
401Payload mismatch or hash verification failure
403XSS attempt detected

Rate limiting

Applies per-IP rate limiting with consecutive failure tracking. See Rate Limiting.

See also: Magic Links for the full custom MFA flow documentation.


Request Enrichment

getFingerPrint

Collects device, browser, geo, and network information from the incoming request and attaches it to req.fingerPrint. Uses @riavzon/bot-detector for User-Agent parsing and IP geolocation.

Import

import { getFingerPrint } from '@riavzon/auth'

Sets

TargetTypeDescription
req.fingerPrint.userAgentstringRaw User-Agent header
req.fingerPrint.ipAddressstringClient IP address
req.fingerPrint.countrystring?Country name
req.fingerPrint.countryCodestring?ISO country code
req.fingerPrint.regionstring?Region/state code
req.fingerPrint.regionNamestring?Region/state full name
req.fingerPrint.citystring?City name
req.fingerPrint.districtstring?City district or subdivision
req.fingerPrint.latstring?Latitude
req.fingerPrint.lonstring?Longitude
req.fingerPrint.timezonestring?IANA timezone
req.fingerPrint.currencystring?Local currency code
req.fingerPrint.ispstring?Internet service provider
req.fingerPrint.orgstring?Organization name
req.fingerPrint.as_orgstring?Autonomous system organization
req.fingerPrint.proxyboolean?Whether the IP is a known proxy
req.fingerPrint.hostingboolean?Whether the IP belongs to a hosting provider
req.fingerPrint.devicestringDevice type
req.fingerPrint.deviceVendorstring?Device manufacturer
req.fingerPrint.deviceModelstring?Device model name
req.fingerPrint.browserstring?Browser name
req.fingerPrint.browserTypestring?Browser category
req.fingerPrint.browserVersionstring?Browser version
req.fingerPrint.osstring?Operating system
req.fingerPrint.botbooleanWhether the client is a known bot
req.fingerPrint.botAIbooleanWhether the client is an AI crawler

Error behavior

If fingerprinting fails (for example, malformed User-Agent or geolocation service unavailable), the middleware logs the error and calls next() without setting req.fingerPrint. Downstream middleware should handle a potentially undefined fingerprint gracefully.

See Fingerprinting for how fingerprint data is used in anomaly detection.


Error Handling

These middleware functions are registered at the end of the middleware stack by bootstrapApp(). They catch unmatched routes and unhandled errors.

notFoundHandler

Returns a standardized JSON 404 response for any request that doesn't match a registered route. Registered after all route handlers.

Response

StatusBody
404{ error: "The page you are looking for doesn't exists" }
This middleware is not exported from @riavzon/auth. It is applied automatically by bootstrapApp().

finalUnHandledErrors

The last-resort Express error handler. Catches any unhandled error that propagates through the middleware stack, logs it, and returns a normalized JSON error response.

Error handler signature

(err: any, req: Request, res: Response, next: NextFunction) => void

Status code logic

  • Uses the existing res.statusCode if it is greater than 415
  • Defaults to 500 otherwise

Response

StatusBody
Varies{ error: string }
This middleware is not exported from @riavzon/auth. It is applied automatically by bootstrapApp().

Logging

httpLogger

A pino-http middleware that logs every HTTP request and response. Writes structured JSON logs to auth-logs/http.log. Automatically redacts the Authorization header and session cookie values in log output.

Features

  • Generates a unique X-Request-Id header for each request (or uses the existing one)
  • Redacts req.headers.authorization and req.cookies.session in logs
  • Skips logging for static asset requests (.css, .js, .png, etc.) and .well-known/ paths
  • Logs IP address, User-Agent, full URL, and cookies as structured context
  • Auto-selects log level based on response status: info for 2xx/3xx, warn for 4xx, error for 5xx
This middleware is not exported from @riavzon/auth. It is applied automatically by bootstrapApp().

The following middleware functions handle magic link verification for built-in flows (MFA and password reset). They are not exported individually but are used internally by the magic link route handlers.

linkMfaVerification

Verifies MFA magic links. On GET, previews the link status and allows a configurable number of views. On POST, consumes the link (single-use) and populates req.link for the downstream verifyMFA handler.

Validation pipeline

  1. Validates query parameters (token, random, reason, visitor) against a Zod schema
  2. Applies per-IP rate limiting with consecutive failure tracking
  3. Verifies the temporary JWT signature and expiry
  4. Checks visitor identity match between URL and token payload
  5. Verifies the random hash using crypto.timingSafeEqual()
  6. Enforces configurable GET/POST usage limits per JTI

linkPasswordVerification

Verifies password reset magic links. Follows the same validation pipeline as linkMfaVerification but uses separate rate limiter instances and threshold configuration (magic_links.thresholds.linkPasswordVerification).


Middleware Stack Order

When bootstrapApp() wires the Express app, middleware is applied in this exact order:

httpLogger

Structured request/response logging with automatic redaction.

Disable x-powered-by

Removes the default Express X-Powered-By header.

helmet

Security headers (CSP, X-Frame-Options, Referrer-Policy, etc.).

headers

No-cache headers for authentication responses.

validateIp

Rejects requests with invalid or missing IP addresses.

hmacAuth (conditional)

HMAC signature verification. Only applied when service.Hmac is configured.

apiVerificationRoute()

Mounts GET /api/public/verify before JSON parsing, cookie parsing, and the bot-detector middleware.

express.json()

Global JSON body parser.

cookieParser()

Cookie parsing.

ApiResponse (Bot Detector)

Mounts the bot detection endpoint at /check.

Route handlers

authenticationRoutes -> tokenRotationRoutes -> magicLinks -> bffAccessRoute -> apiProtectedRoutes -> /operational/config

notFoundHandler

Catches unmatched routes.

finalUnHandledErrors

Last-resort error handler.

Logo