Skip to main content

MastraAuthBetterAuth Class

The MastraAuthBetterAuth class 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
Direct link to 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 to mount the /api/auth/* route (or your configured base path).

Installation
Direct link to Installation

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

npm install @mastra/auth-better-auth

Usage example
Direct link to Usage example

First, create your Better Auth instance:

lib/auth.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:

src/mastra/index.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 for all available configuration options.

Custom authorization
Direct link to Custom authorization

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

Route configuration
Direct link to Route configuration

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

Matching rules
Direct link to 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
Direct link to 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.

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:

src/mastra/index.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:

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:

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
Direct link to 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:

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 for more configuration options.

Making authenticated requests
Direct link to Making authenticated requests

src/components/test-agent.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>
}

Troubleshooting
Direct link to 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.