@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 match | Behavior |
|---|---|
authenticate: false | Bypass — skip JWT validation entirely. |
authenticate: true | Require — valid JWT or 401. |
| No matching policy | Controlled 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):
- (Opt-in) When
enableDevTokensistrue, try HMAC verification with the dev secret (fast, in-process). - 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?
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
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") }));