Guards, CSRF, Rate Limits, and Turnstile | Kyzen | Primitives Docs

Guards, CSRF, Rate Limits, and Turnstile

Apply runtime auth controls before route handlers run

These subsystems live as nested options on kyzenAuthMiddleware({...}). They run inside the auth middleware's pre-route check phase before route handlers: rate-limit, CSRF/origin checks, Turnstile, then guards.

// src/middleware.ts
kyzenAuthMiddleware({
  guards:    { /* see Guards below */ },
  csrf:      { /* see CSRF below */ },
  rateLimit: { /* see Rate limiting below */ },
  turnstile: { /* see Turnstile below */ },
});

Guards

Guards are configured with:

  • paths
  • exclude
  • redirectTo

Example:

guards: {
  paths: ['/admin/*', '/dashboard/*'],
  exclude: ['/auth/login'],
  redirectTo: '/auth/login',
}

Runtime helpers:

  • checkGuard()
  • requireAuthGuard

Important behavior:

checkGuard() only checks for the presence of the session cookie. It does not fully validate the session record. Route code should still call getCurrentUser() or getAuth().getSession() before trusting the request as authenticated.

CSRF and origin checks

CSRF protection is opt-in because existing apps may use different credential route names. Configure it on routes that mutate auth state, such as sign-in and sign-up form actions:

csrf: {
  paths: ['/auth/signin', '/auth/signup'],
  trustedOrigins: ['https://app.example.com', 'https://*.tenant.example.com'],
}

Behavior:

  • Cross-site top-level POST navigations without cookies are rejected using Fetch Metadata headers.
  • Unsafe requests with cookies validate the Origin header against the request origin plus trustedOrigins.
  • Wildcard trusted origins match tenant subdomains.

Runtime helpers:

  • checkCsrf()
  • configureCsrf(config)

Rate limiting

Configure rate limits per route:

rateLimit: {
  defaultWindowMs: 60000,
  defaultMaxRequests: 10,
  kvBinding: 'RATE_LIMIT',
  routes: [
    {
      id: 'auth-signin',
      path: '/auth/signin',
      methods: ['POST'],
      maxRequests: 5,
      windowMs: 60000,
      message: 'Too many sign-in attempts.',
    },
  ],
}

Runtime helpers:

  • checkRateLimit()
  • getRateLimitInfo(routeId)

The limiter uses cf-connecting-ip and can persist counts in KV when configured, with an in-memory fallback per isolate.

Turnstile

Configure Turnstile with:

  • secretEnv
  • siteKeyEnv
  • skipInDev
  • routes

Example:

turnstile: {
  secretEnv: 'TURNSTILE_SECRET',
  siteKeyEnv: 'TURNSTILE_SITE_KEY',
  routes: [
    {
      id: 'auth-signin',
      path: '/auth/signin',
      methods: ['POST'],
      expectedAction: 'signin',
    },
  ],
}

Runtime helpers:

  • checkTurnstile()
  • verifyTurnstile(token, options)

The verifier reads tokens from common headers or form fields, then calls Cloudflare's Turnstile verification endpoint.