> ## Documentation Index
> Fetch the complete documentation index at: https://wemstudios.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication Middleware: Bearer, Basic Auth, and JWT

> Three authentication middleware modules for Blaze: bearerAuth for API tokens, basicAuth for credentials, and jwtAuth for HS256/RS256 JWT verification.

Blaze ships three authentication middleware modules so you can secure routes without pulling in third-party libraries. Use **`bearerAuth`** when your clients authenticate with a pre-shared API key or token. Use **`basicAuth`** for human-facing admin interfaces or simple service-to-service authentication. Use **`jwtAuth`** when tokens are issued by an identity provider and carry claims you need downstream — it verifies signatures entirely via Web Crypto (`crypto.subtle`), with no external dependencies.

<Warning>
  Never store secrets in `wrangler.toml` `[vars]`. Variables in `[vars]` are bundled into the Worker and visible in Cloudflare's dashboard. Use **Cloudflare Secrets** (`wrangler secret put MY_SECRET`) for anything sensitive — secrets are encrypted at rest and injected into `req.env` at runtime exactly like vars, but are never exposed in plaintext.
</Warning>

## Bearer token auth

`bearerAuth` extracts the `Authorization: Bearer <token>` header and validates it. It supports three validation strategies: a static token string, a dynamic secret resolver (useful when your token lives in KV or an environment binding), and a fully custom async validator.

All token comparisons use a **constant-time equality check** to prevent timing attacks.

```typescript theme={null}
import { bearerAuth } from 'blaze/middleware/bearer-auth'
```

### `BearerAuthOptions`

| Option      | Type                                           | Description                                                                     |
| ----------- | ---------------------------------------------- | ------------------------------------------------------------------------------- |
| `token`     | `string`                                       | Static token to compare against.                                                |
| `secret`    | `string \| (req) => string \| Promise<string>` | Resolve the expected token at request time (e.g. from a KV binding or env var). |
| `validator` | `(token, req) => boolean \| Promise<boolean>`  | Fully custom async validator — return `true` to allow the request.              |
| `realm`     | `string`                                       | `WWW-Authenticate` realm sent on 401. Default: `'Blaze'`.                       |

<CodeGroup>
  ```typescript Static secret theme={null}
  import { createApp } from 'blaze'
  import { bearerAuth } from 'blaze/middleware/bearer-auth'

  const app = createApp<Env>()

  // Compare against a Cloudflare Secret injected as an env var
  app.use(
    '/api',
    bearerAuth({
      secret: (req) => req.env.API_SECRET,
    }),
  )

  app.get('/api/data', (req, res) => {
    res.json({ ok: true })
  })
  ```

  ```typescript Dynamic KV validator theme={null}
  import { createApp } from 'blaze'
  import { bearerAuth } from 'blaze/middleware/bearer-auth'

  const app = createApp<Env>()

  // Look up valid tokens stored in KV — useful for multi-tenant APIs
  app.use(
    '/api',
    bearerAuth({
      validator: async (token, req) => {
        const record = await req.env.API_KEYS_KV.get(token, { type: 'json' }) as
          | { active: boolean; accountId: string }
          | null

        if (!record?.active) return false

        // Attach account info for downstream handlers
        ;(req as any).accountId = record.accountId
        return true
      },
    }),
  )
  ```
</CodeGroup>

On a missing or invalid token, `bearerAuth` responds with `401 Unauthorized` and sets the `WWW-Authenticate: Bearer realm="Blaze"` response header automatically.

## HTTP Basic Auth

`basicAuth` decodes the `Authorization: Basic <base64>` header, splits the colon-delimited credentials, and compares them against the expected username and password using a constant-time comparison.

```typescript theme={null}
import { basicAuth } from 'blaze/middleware/basic-auth'
```

### `BasicAuthOptions`

| Option     | Type                                           | Description                                             |
| ---------- | ---------------------------------------------- | ------------------------------------------------------- |
| `username` | `string \| (req) => string \| Promise<string>` | Expected username — static or resolved at request time. |
| `password` | `string \| (req) => string \| Promise<string>` | Expected password — static or resolved at request time. |
| `realm`    | `string`                                       | `WWW-Authenticate` realm. Default: `'Blaze'`.           |

```typescript theme={null}
import { createApp } from 'blaze'
import { basicAuth } from 'blaze/middleware/basic-auth'

type Env = {
  ADMIN_USER: string // Cloudflare Secret
  ADMIN_PASS: string // Cloudflare Secret
}

const app = createApp<Env>()

// Protect all /admin/* routes with env-backed credentials
app.use(
  '/admin',
  basicAuth({
    username: (req) => req.env.ADMIN_USER,
    password: (req) => req.env.ADMIN_PASS,
    realm: 'Admin Panel',
  }),
)

app.get('/admin/dashboard', (req, res) => {
  res.html('<h1>Admin Dashboard</h1>')
})
```

When credentials are missing or wrong, `basicAuth` responds with `401` and sets `WWW-Authenticate: Basic realm="Admin Panel"`, causing browsers to show their native credential prompt.

## JWT auth

`jwtAuth` verifies JSON Web Tokens using the Web Crypto API — no `jsonwebtoken` or `jose` packages required. It supports **HS256** (HMAC-SHA256 shared secret) and **RS256** (RSA-SHA256 public key), and validates `exp`, `nbf`, `iss`, and `aud` claims automatically. After successful verification, it sets the decoded payload on `req.user`.

```typescript theme={null}
import { jwtAuth } from 'blaze/middleware/jwt'
```

### `JwtAuthOptions`

| Option       | Type                                           | Description                                                                                                 |
| ------------ | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `secret`     | `string \| (req) => string \| Promise<string>` | HMAC secret (HS256) or PEM/JWK public key string (RS256). Resolved per-request so you can pull it from env. |
| `algorithms` | `('HS256' \| 'RS256')[]`                       | Algorithms to accept. Default: `['HS256']`.                                                                 |
| `issuer`     | `string`                                       | Expected `iss` claim. Rejected if it doesn't match.                                                         |
| `audience`   | `string`                                       | Expected `aud` claim. Rejected if it doesn't match.                                                         |

<CodeGroup>
  ```typescript HS256 (shared secret) theme={null}
  import { createApp } from 'blaze'
  import { jwtAuth } from 'blaze/middleware/jwt'

  type Env = { JWT_SECRET: string }

  const app = createApp<Env>()

  app.use(
    '/api',
    jwtAuth({
      secret: (req) => req.env.JWT_SECRET,
      algorithms: ['HS256'],
      issuer: 'https://auth.myapp.com',
      audience: 'myapp-api',
    }),
  )

  // req.user is now the decoded JWT payload
  app.get('/api/me', (req, res) => {
    res.json({ user: (req as any).user })
  })
  ```

  ```typescript RS256 (public key) theme={null}
  import { createApp } from 'blaze'
  import { jwtAuth } from 'blaze/middleware/jwt'

  type Env = { RS256_PUBLIC_KEY: string } // PEM or JWK string

  const app = createApp<Env>()

  app.use(
    '/api',
    jwtAuth({
      // Pass either a PEM-encoded SPKI public key or a JSON Web Key string
      secret: (req) => req.env.RS256_PUBLIC_KEY,
      algorithms: ['RS256'],
      issuer: 'https://idp.example.com',
    }),
  )

  app.get('/api/profile', (req, res) => {
    const user = (req as any).user as { sub: string; email: string }
    res.json({ sub: user.sub, email: user.email })
  })
  ```
</CodeGroup>

The `jwtAuth` middleware accepts the public key in either **PEM (SPKI)** format (a string beginning with `-----BEGIN PUBLIC KEY-----`) or as a **JSON Web Key** string (a stringified JWK object). For RS256, store the public key as a Cloudflare Secret — even though it's public, keeping it in `req.env` makes rotation easy without redeploying.

If the token is missing, expired, signed with the wrong algorithm, or fails signature verification, `jwtAuth` responds with `401` and a descriptive error message.

## Protecting routes

<Steps>
  <Step title="Global API protection">
    Apply auth to every route under a path prefix using `app.use()`:

    ```typescript theme={null}
    // All requests to /api/* must carry a valid JWT
    app.use('/api', jwtAuth({ secret: (req) => req.env.JWT_SECRET }))

    app.get('/api/users', listUsers)
    app.post('/api/orders', createOrder)
    ```
  </Step>

  <Step title="Per-route auth">
    Inline middleware on individual routes for mixed public/private APIs:

    ```typescript theme={null}
    const requireAuth = jwtAuth({ secret: (req) => req.env.JWT_SECRET })

    app.get('/products', listProducts)       // public
    app.post('/products', requireAuth, createProduct) // protected
    app.delete('/products/:id', requireAuth, deleteProduct)
    ```
  </Step>

  <Step title="Sub-router with shared auth">
    Use a Router to apply one auth strategy to a group of routes without touching global middleware:

    ```typescript theme={null}
    const admin = app.Router()

    admin.use(basicAuth({
      username: (req) => req.env.ADMIN_USER,
      password: (req) => req.env.ADMIN_PASS,
    }))

    admin.get('/users', listAllUsers)
    admin.delete('/users/:id', deleteUser)

    app.use('/admin', admin)
    ```
  </Step>
</Steps>

## TypeScript: typed `req.user`

`jwtAuth` sets the decoded payload on `req.user` at runtime, but TypeScript doesn't know about it by default. Augment the `BlazeRequest` interface in a declaration file to make `req.user` fully typed throughout your project.

```typescript theme={null}
// src/types/blaze.d.ts

declare module 'blaze' {
  interface BlazeRequest {
    /** Decoded JWT payload — set by jwtAuth middleware. */
    user?: {
      sub: string
      email: string
      roles: string[]
      iat: number
      exp: number
    }
  }
}
```

After adding this declaration, TypeScript will type-check every access to `req.user` in your route handlers — no casting required.

```typescript theme={null}
app.get('/api/me', (req, res) => {
  // req.user is now typed — no (req as any).user needed
  if (!req.user) return res.status(401).json({ error: 'Unauthorized' })
  res.json({ sub: req.user.sub, email: req.user.email })
})
```
