Search docs

Jump between documentation pages.

Authentication & authorization

DaloyJS doesn't bundle a user database or login UI — instead, it ships primitives that make it easy to plug in a hosted identity provider (IdP). Your API receives a bearer token, verifies it with the provider's JWKS or SDK, and gates routes by scope, role, or organization. The pages in this section show how to wire up the five most common IdPs.

Supported providers

  • AWS Cognito — pay-as-you-go user pools with hosted sign-in. Use aws-jwt-verify to verify access and ID tokens with zero runtime dependencies; runs on Node, edge, and Lambda.
  • Microsoft Entra ID (MSAL) — enterprise SSO for Microsoft 365 / Azure AD users. Verify tokens with the OIDC JWKS using jose; acquire downstream tokens with @azure/msal-node when needed.
  • Auth0 — developer-friendly IdP with universal login, MFA, and rich rule engine. Verify access tokens with joseagainst your tenant's issuer URL.
  • Okta — workforce identity with custom authorization servers and granular policies. Use the official @okta/jwt-verifier for access and ID tokens.
  • Clerk — modern, embeddable authentication with user, organization, and billing primitives. Use @clerk/backend authenticateRequest() to authenticate any Request.

Runtime compatibility at a glance

ProviderNode / Bun / DenoCloudflare WorkersVercel EdgeAWS Lambda
AWS Cognito (aws-jwt-verify)YesYes (Web Crypto)YesYes
Entra ID (jose)YesYesYesYes
Auth0 (jose)YesYesYesYes
Okta (@okta/jwt-verifier)YesNo (Node-only)NoYes
Clerk (@clerk/backend)YesYesYesYes

Common pattern

Each provider page implements the same three steps: install the verifier SDK, register a DaloyJS plugin that decorates the request context with an auth object, then guard routes with a small middleware that requires a token (and optional scopes).

// src/plugins/auth.ts
import type { App, Middleware } from "@daloyjs/core";

export interface Principal {
  sub: string;
  scopes?: string[];
  claims: Record<string, unknown>;
}

export interface TokenVerifier {
  verify(token: string): Promise<Principal>;
}

export function authPlugin(verifier: TokenVerifier) {
  return {
    name: "auth",
    register(app: App) {
      app.decorate("verifier", verifier);
    },
  };
}

export function requireAuth(...requiredScopes: string[]): Middleware {
  return async (ctx, next) => {
    const header = ctx.request.headers.get("authorization") ?? "";
    const [scheme, token] = header.split(" ");
    if (scheme?.toLowerCase() !== "bearer" || !token) {
      return ctx.problem(401, "unauthorized", "Missing bearer token");
    }
    try {
      const principal = await ctx.state.verifier.verify(token);
      if (requiredScopes.length) {
        const scopes = principal.scopes ?? [];
        const ok = requiredScopes.every((s) => scopes.includes(s));
        if (!ok) return ctx.problem(403, "forbidden", "Insufficient scope");
      }
      ctx.state.principal = principal;
      return next();
    } catch {
      return ctx.problem(401, "unauthorized", "Invalid or expired token");
    }
  };
}

declare module "@daloyjs/core" {
  interface AppState {
    verifier: TokenVerifier;
    principal?: Principal;
  }
}

Each provider page implements TokenVerifier with the official SDK so the rest of your application stays IdP-agnostic.

Security checklist

  • Always verify the signature.Never trust an unverified JWT — decode-only utilities are for debugging. Use the provider's JWKS endpoint with key caching and automatic rotation (every SDK on the following pages handles this).
  • Check iss and aud. Pin the expected issuer URL and audience/client ID. A correct signature on the wrong audience is still a token confusion attack.
  • Authorize, don't just authenticate. A valid token only proves the caller is who they say they are. Enforce scopes, roles, or organization membership for every privileged action.
  • Use TLS everywhere. Bearer tokens are plaintext-equivalent. Require HTTPS and set the secureHeaders middleware (Strict-Transport-Security).
  • Rate-limit token-issuing routes. Login redirects, token-exchange endpoints, and any introspection passthroughs should go through rateLimit (or the Redis store) so abuse can't drive cost or lock out users.
  • Protect cookies and CSRF. If you also use session cookies (for an admin panel, say), enable CSRF and use SameSite=Lax + Secure + HttpOnly via the built-in session middleware.