---
title: "createAuthMiddleware"
description: "Framework-agnostic factory with route filtering, session timing, and lifecycle hooks. Call it once at startup and reuse across requests."
canonical_url: "https://www.evlog.dev/use-cases/better-auth/middleware"
---
# createAuthMiddleware

> Framework-agnostic factory with route filtering, session timing, and lifecycle hooks. Call it once at startup and reuse across requests.

`createAuthMiddleware` wraps `identifyUser` with the things you need on every request: route filtering, session resolution timing, lifecycle hooks, and silent error handling. Call it once at startup, then use the returned function in your framework's middleware/hook system.

```typescript [server/middleware/auth-identify.ts]
import { createAuthMiddleware } from 'evlog/better-auth'

const identify = createAuthMiddleware(auth, {
  exclude: ['/api/auth/**', '/api/public/**'],
  include: ['/api/**'],
  maskEmail: true,
})
```

The function signature is `(log, headers, path?) => Promise<boolean>`. It resolves the session, calls `identifyUser`, captures timing into `auth.resolvedIn`, fires lifecycle hooks, and silently catches errors so session resolution never breaks a request.

## Options

Inherits all [`identifyUser` options](https://www.evlog.dev/use-cases/better-auth/identify-user#options), plus:

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        exclude
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      <code>
        ['/api/auth/**']
      </code>
    </td>
    
    <td>
      Route patterns to skip (glob).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        include
      </code>
    </td>
    
    <td>
      <code>
        string[]
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      If set, only matching routes are resolved.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        onIdentify
      </code>
    </td>
    
    <td>
      <code>
        (log, session) => void
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Called after successful identification.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        onAnonymous
      </code>
    </td>
    
    <td>
      <code>
        (log) => void
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Called when no session is found.
    </td>
  </tr>
</tbody>
</table>

## Skip the routes that do not need a session

Skip Better Auth's own routes and any public endpoints to avoid wasted database queries:

```typescript
const identify = createAuthMiddleware(auth, {
  exclude: [
    '/api/auth/**',     // Better Auth itself
    '/api/public/**',   // Public endpoints
    '/api/health',      // Health checks
  ],
})
```

For high-traffic apps, flip the model and only resolve sessions on routes that need them:

```typescript
const identify = createAuthMiddleware(auth, {
  include: ['/api/dashboard/**', '/api/account/**'],
})
```

`include` and `exclude` use glob patterns (`*`, `**`). Provide both if you need granular control, and `exclude` wins over `include`.

## React to identification

Use `onIdentify` to react to user identification, for example to force-keep logs for premium users via tail sampling:

```typescript [server/middleware/auth-identify.ts]
const identify = createAuthMiddleware(auth, {
  onIdentify: (log, session) => {
    if (session.user.plan === 'enterprise') {
      log.set({ _forceKeep: true })
    }
  },
  onAnonymous: (log) => {
    log.set({ anonymous: true })
  },
})
```

Hooks fire after the session is resolved and `identifyUser` has set its fields. They run on every request that passes the `include`/`exclude` filter, so keep them fast and side-effect-free.

<tip>

Common patterns for `onIdentify`:

- Force-keep audit logs for admins or high-value plans.
- Tag the request with feature flags or tenant info loaded from the session.
- Increment a per-user counter for billing.

</tip>

## What happens when auth fails

The middleware catches every error from `getSession` and logs nothing, so your request keeps flowing whether the auth backend is up or down. The wide event still includes `auth.resolvedIn` and `auth.identified: false` so you can alert on session resolution health from your dashboards.

---

- [Identify User](https://www.evlog.dev/use-cases/better-auth/identify-user)
- [Performance](https://www.evlog.dev/use-cases/better-auth/performance)


## Sitemap

See the full [sitemap](https://www.evlog.dev/sitemap.md) for all pages.
