Skip to content
Eugene Lazutkin edited this page Jul 17, 2026 · 1 revision

fetch

The Fetch port of the middleware family: cognito-toolkit/fetch, for (request: Request) => Promise<Response> servers — Bun.serve, Deno.serve / Deploy, Cloudflare Workers, Hono, and dynamodb-toolkit's createFetchAdapter handlers.

import {makeAuth} from 'cognito-toolkit/fetch';

function makeAuth<Payload extends object = JwtPayload>(options: AuthOptions<Payload>): Auth<Payload>;

type FetchHandler = (request: Request, ...rest: any[]) => Response | Promise<Response>;

interface Auth<Payload> {
  getUser(request: Request): Promise<AuthUser<Payload> | null>; // memoized per Request
  isAuthenticated(handler: FetchHandler): FetchHandler;
  hasGroup(group: string): (handler: FetchHandler) => FetchHandler;
  hasScope(scope: string): (handler: FetchHandler) => FetchHandler;
  isAllowed(validator: (request: Request, groups: string[], scopes: string[]) => boolean | Promise<boolean>): (handler: FetchHandler) => FetchHandler;
  setAuthCookie(request: Request, response: Response, cookieOptions?: CookieOptions): Promise<Response>;
}

type AuthUser<Payload> = Payload & {_token: string};

Options: see Middleware ports § Options — no stateUserProperty here. Only standard Request / Response types are involved; the payload type flows from the verifier.

Example

import {CognitoJwtVerifier} from 'cognito-toolkit';
import {makeAuth} from 'cognito-toolkit/fetch';

const verifier = CognitoJwtVerifier.create({userPoolId: 'us-east-1_MY_POOL', clientId: 'my-app-client', tokenUse: 'access'});
const auth = makeAuth({verifier});

export default {
  fetch: auth.isAuthenticated(async request => {
    const user = await auth.getUser(request); // memoized — already verified by the guard
    return Response.json(user);
  })
};

// per-route guards wrap individual handlers:
const writers = auth.hasGroup('writers')(async request => new Response('ok'));

// composes directly with dynamodb-toolkit:
// Bun.serve({fetch: auth.isAuthenticated(createFetchAdapter(adapter, {mountPath: '/api'}))});

Port specifics

  • Requests are immutable, so there is no state property: getUser(request) is a WeakMap-memoized lookup — guards and handlers share one verification per request, extra calls are free.
  • Guards preserve extra server args (Bun's server, Deno's info, Cloudflare's env / ctx): whatever the server passes after request reaches the wrapped handler untouched.
  • Denials are new Response(null, {status: 401 | 403}).
  • setAuthCookie is async and returns the response to use — it clones when the original's headers are immutable (e.g. a response obtained from fetch()). Cookies are serialized in-house: Path=/ and HttpOnly by default, Domain from the request hostname.

Clone this wiki locally