# Better Auth

The `@mastra/auth-better-auth` package provides authentication for Mastra using Better Auth. It verifies incoming requests using your Better Auth instance and integrates with the Mastra server via the `server.auth` option.

## Prerequisites

This example uses Better Auth. Make sure your Better Auth instance is configured and your environment variables are set.

```env
# Required by Better Auth
BETTER_AUTH_SECRET=...   # at least 32 chars
BETTER_AUTH_URL=http://localhost:3000

# Example DB URL used by the snippet below (adjust for your setup)
DATABASE_URL=postgres://...
```

> **Note:** Better Auth recommends setting `baseURL` explicitly (or via `BETTER_AUTH_URL`) for security and stability.

If you haven't mounted Better Auth's handler yet (so your app can sign users in / create sessions), follow the [Better Auth installation guide](https://www.better-auth.com/docs/installation) to mount the `/api/auth/*` route (or your configured base path).

## Installation

Install the `@mastra/auth-better-auth` package:

**npm**:

```bash
npm install @mastra/auth-better-auth
```

**pnpm**:

```bash
pnpm add @mastra/auth-better-auth
```

**Yarn**:

```bash
yarn add @mastra/auth-better-auth
```

**Bun**:

```bash
bun add @mastra/auth-better-auth
```

## Usage example

First, create your Better Auth instance:

```ts
import { betterAuth } from 'better-auth'

export const auth = betterAuth({
  database: {
    provider: 'postgresql',
    url: process.env.DATABASE_URL!,
  },
  emailAndPassword: {
    enabled: true,
  },
  baseURL: process.env.BETTER_AUTH_URL,
  secret: process.env.BETTER_AUTH_SECRET,
})
```

Then, use it with Mastra:

```ts
import { Mastra } from '@mastra/core'
import { MastraAuthBetterAuth } from '@mastra/auth-better-auth'
import { auth } from '@/lib/auth'

const mastraAuth = new MastraAuthBetterAuth({
  auth,
})

export const mastra = new Mastra({
  server: {
    auth: mastraAuth,
  },
})
```

> **Info:** Visit [MastraAuthBetterAuth](https://mastra.ai/reference/auth/better-auth) for all available configuration options.

## Custom authorization

```ts
const mastraAuth = new MastraAuthBetterAuth({
  auth,
  async authorizeUser(user) {
    // Example: only allow verified emails
    return user?.user?.emailVerified === true
  },
})
```

## Route configuration

```ts
const mastraAuth = new MastraAuthBetterAuth({
  auth,
  public: ['/health', '/api/status'],
  protected: ['/api/*', '/admin/*'],
})
```

### Matching rules

- `public` and `protected` accept exact paths, wildcard patterns (like `/api/*`), and path params (like `/users/:id`).
- For method-specific rules, use tuples like `["/api/agents", ["GET", "POST"]]`.
- If a route matches both `public` and `protected`, `public` wins and no auth is required.
- If neither matches, routes are treated as protected by default (unless a route is explicitly marked `requiresAuth: false`).

## Client-side setup

When auth is enabled, requests to Mastra's built-in routes require authentication. In practice, that means your client needs to send whatever credential your Better Auth setup uses for authenticated requests.

### Cookie session (recommended)

If your Better Auth setup uses cookies, configure the client to send credentials. For cross-origin requests (e.g. Next.js on `:3000` calling Mastra on `:4111`), enable CORS credentials on the Mastra server:

```ts
export const mastra = new Mastra({
  server: {
    auth: mastraAuth,
    cors: {
      origin: 'http://localhost:3000', // your frontend origin
      credentials: true,
    },
  },
})
```

Then configure the client to include credentials:

```ts
import { MastraClient } from '@mastra/client-js'

export const mastraClient = new MastraClient({
  baseUrl: 'http://localhost:4111',
  credentials: 'include',
})
```

If you are calling the API directly, include credentials in `fetch` as well:

```ts
await fetch('http://localhost:4111/api/agents/weatherAgent/generate', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  credentials: 'include',
  body: JSON.stringify({ messages: 'Weather in London' }),
})
```

### Bearer token

You can pass the signed session token as a Bearer token. Retrieve it from your Better Auth client session and include it in the `Authorization` header:

```ts
import { MastraClient } from '@mastra/client-js'
import { authClient } from './auth-client' // your Better Auth client

const session = await authClient.getSession()

export const mastraClient = new MastraClient({
  baseUrl: 'http://localhost:4111',
  headers: {
    Authorization: `Bearer ${session.data?.session.token}`,
  },
})
```

> **Info:** Visit [Mastra Client SDK](https://mastra.ai/docs/server/mastra-client) for more configuration options.

### Making authenticated requests

**React**:

```tsx
import { mastraClient } from '../lib/mastra-client'

export const TestAgent = () => {
  async function handleClick() {
    const agent = mastraClient.getAgent('weatherAgent')

    const response = await agent.generate('Weather in London')

    console.log(response)
  }

  return <button onClick={handleClick}>Test Agent</button>
}
```

**cURL**:

```bash
curl -X POST http://localhost:4111/api/agents/weatherAgent/generate \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your-token>" \
  -d '{
    "messages": "Weather in London"
  }'
```

## Troubleshooting

- **401 on every request**: confirm your Better Auth handler is mounted and your app can create a valid session. Check that your client sends either a session cookie or an `Authorization: Bearer <signed-token>` header.
- **Cookies not sent cross-origin**: set `credentials: "include"` in `MastraClient` and configure `server.cors` with your frontend origin and `credentials: true`.
- **Bearer token rejected**: ensure you pass the full signed session token (from `authClient.getSession()`), not a raw or unsigned token.
- **Base URL issues**: set `baseURL` in `betterAuth({ ... })` or set `BETTER_AUTH_URL`.
- **DB connection errors**: verify `DATABASE_URL` and database provider configuration.