Skip to content

@adrianhall/cloudflare-toolkit


@adrianhall/cloudflare-toolkit / lib/hono / cloudflareAccess

Function: cloudflareAccess()

cloudflareAccess(options?): MiddlewareHandler<{ Variables: AuthVariables; }>

Defined in: src/lib/hono/cloudflare-access.ts:309

Create a Hono middleware that validates a Cloudflare Access JWT and sets AuthVariables (Cloudflare_Access_Identity, ./types.ts) on the Hono context.

Policy evaluation:

Policy matchBehavior
authenticate: falseBypass — skip JWT validation entirely.
authenticate: trueRequire — valid JWT or 401.
No matching policyControlled by defaultAction (see below).

Every 401 this middleware returns is an RFC 9457 application/problem+json response ({ type, status, title, detail }), matching problemDetailsErrorHandler and notFoundHandler's conventions.

defaultAction (applies when no policy matches):

  • "block" (default) — return 401 if no valid JWT is present.
  • "bypass" — allow the request through. If a JWT is present and valid, the context variables are still set; otherwise the request continues with no authenticated user.

Verification order (when JWT validation is performed):

  1. (Opt-in) When enableDevTokens is true, try HMAC verification with the dev secret (fast, in-process).
  2. Verify against the remote JWKS endpoint for the team domain.

Developer-token verification is fail-closed: it is disabled by default so a deployed Worker never silently trusts a forgeable HS256 token signed with the public DEFAULT_DEV_SECRET. Enable it only in local development.

Path-specific audiences: a matched PathPolicy's own audience overrides CloudflareAccessOptions.audience for that request, so one middleware instance can protect several path-scoped Cloudflare Access applications on the same hostname — each validated against its own Audience Tag — instead of one flat allowlist that would let a token minted for one application pass audience validation on another application's routes.

Audience validation is opt-in, not fail-closed: a request whose matched policy (or the top-level fallback, when no policy applies) has no audience configured skips the aud check for that request and allows cross-application token replay within the same Cloudflare Access team (see CloudflareAccessOptions.audience's docs). To surface this without breaking existing deployments, cloudflareAccess logs a one-time warning at construction time whenever some authenticated request path could still reach verification with no audience configured and enableDevTokens is not true — i.e. in the default, production-shaped configuration. A fully audience-covered policy set (every authenticated policy sets its own audience, and defaultAction is "bypass") does not trigger this warning even without a top-level fallback. The warning is intentionally silent when enableDevTokens is true, since that already signals a local-development posture.

Parameters

options?

CloudflareAccessOptions = {}

Options controlling path policies, the default action for unmatched paths, the Cloudflare Access team domain/audience, dev-token verification, and the logger.

Returns

MiddlewareHandler<{ Variables: AuthVariables; }>

A Hono MiddlewareHandler parameterised with AuthVariables, so c.set("Cloudflare_Access_Identity", …) inside this middleware — and c.get("Cloudflare_Access_Identity") in a consumer's own handlers once composed via app.use(...) — are statically checked against AuthVariables rather than accepted as untyped magic strings.

Remarks

Security-critical: this fail-closed default must be preserved exactly — see the "fail-closed" describe block in test/workers/hono/cloudflare-access.test.ts.

Example

ts
import { Hono } from "hono";
import { cloudflareAccess, type AuthVariables } from "@adrianhall/cloudflare-toolkit/hono";

const app = new Hono<{ Variables: AuthVariables }>();
app.use(cloudflareAccess({ policies: [{ pattern: /^/api/version$/, authenticate: false }] }));
app.get("/api/*", (c) => c.json({ user: c.get("Cloudflare_Access_Identity") }));