better-auth is a self-hosted, open-source authentication framework that gives you complete control over your authentication system.
Installation
npm install @mastra/auth-better-auth better-auth
Configuration
Create better-auth instance
import { betterAuth } from 'better-auth' ;
export const auth = betterAuth ({
database: {
provider: 'postgresql' ,
url: process . env . DATABASE_URL ! ,
},
emailAndPassword: {
enabled: true ,
},
});
Import MastraAuthBetterAuth
import { MastraAuthBetterAuth } from '@mastra/auth-better-auth' ;
import { Mastra } from '@mastra/core' ;
const mastraAuth = new MastraAuthBetterAuth ({
auth , // Pass your better-auth instance
});
const mastra = new Mastra ({
server: {
auth: mastraAuth ,
},
});
Mount better-auth endpoints
import { Hono } from 'hono' ;
import { MastraServer } from '@mastra/hono' ;
const app = new Hono ();
// Mount better-auth API routes
app . all ( '/api/auth/**' , async ( c ) => {
return auth . handler ( c . req . raw );
});
const server = new MastraServer ({ mastra , app });
Configuration Options
better-auth instance created with betterAuth()
name
string
default: "better-auth"
Provider name identifier
Complete Example
import { betterAuth } from 'better-auth' ;
import { Hono } from 'hono' ;
import { Mastra } from '@mastra/core' ;
import { MastraAuthBetterAuth } from '@mastra/auth-better-auth' ;
import { MastraServer } from '@mastra/hono' ;
// Create better-auth instance
const auth = betterAuth ({
database: {
provider: 'postgresql' ,
url: process . env . DATABASE_URL ! ,
},
emailAndPassword: {
enabled: true ,
},
socialProviders: {
github: {
clientId: process . env . GITHUB_CLIENT_ID ! ,
clientSecret: process . env . GITHUB_CLIENT_SECRET ! ,
},
},
});
// Create Mastra auth provider
const mastraAuth = new MastraAuthBetterAuth ({ auth });
// Create Mastra instance
const mastra = new Mastra ({
server: {
auth: mastraAuth ,
},
});
// Create Hono app
const app = new Hono ();
// Mount better-auth endpoints
app . all ( '/api/auth/**' , async ( c ) => {
return auth . handler ( c . req . raw );
});
// Mount Mastra server (protected)
const server = new MastraServer ({ mastra , app });
Authentication Methods
Email/Password
const auth = betterAuth ({
database: {
provider: 'postgresql' ,
url: process . env . DATABASE_URL ! ,
},
emailAndPassword: {
enabled: true ,
requireEmailVerification: true ,
},
});
Social OAuth
const auth = betterAuth ({
database: {
provider: 'postgresql' ,
url: process . env . DATABASE_URL ! ,
},
socialProviders: {
github: {
clientId: process . env . GITHUB_CLIENT_ID ! ,
clientSecret: process . env . GITHUB_CLIENT_SECRET ! ,
},
google: {
clientId: process . env . GOOGLE_CLIENT_ID ! ,
clientSecret: process . env . GOOGLE_CLIENT_SECRET ! ,
},
},
});
Magic Link
const auth = betterAuth ({
database: {
provider: 'postgresql' ,
url: process . env . DATABASE_URL ! ,
},
plugins: [
magicLink ({
sendMagicLink : async ({ email , url }) => {
// Send email with magic link
await sendEmail ( email , url );
},
}),
],
});
Client Integration
React Client
import { createAuthClient } from 'better-auth/react' ;
import { useEffect , useState } from 'react' ;
const authClient = createAuthClient ({
baseURL: 'http://localhost:3000' ,
});
export default function App () {
const [ session , setSession ] = useState ( null );
useEffect (() => {
authClient . getSession (). then (({ data }) => {
setSession ( data );
});
}, []);
const signIn = async () => {
await authClient . signIn . email ({
email: '[email protected] ' ,
password: 'password' ,
});
};
const signOut = async () => {
await authClient . signOut ();
};
if ( ! session ) {
return < button onClick = { signIn } > Sign in </ button > ;
}
return (
< div >
< p > Welcome { session . user . email } </ p >
< button onClick = { signOut } > Sign out </ button >
< MyMastraComponent />
</ div >
);
}
Making Authenticated Requests
import { createAuthClient } from 'better-auth/react' ;
const authClient = createAuthClient ({
baseURL: 'http://localhost:3000' ,
});
async function callMastraAPI () {
const { data : session } = await authClient . getSession ();
if ( ! session ) {
throw new Error ( 'Not authenticated' );
}
const response = await fetch ( 'http://localhost:4111/api/mastra/agents' , {
headers: {
'Authorization' : `Bearer ${ session . session . token } ` ,
'Cookie' : document . cookie , // better-auth also supports cookie-based sessions
},
});
return response . json ();
}
User Object
interface BetterAuthUser {
session : {
id : string ;
userId : string ;
token : string ;
expiresAt : Date ;
};
user : {
id : string ;
email : string ;
emailVerified : boolean ;
name ?: string ;
image ?: string ;
createdAt : Date ;
updatedAt : Date ;
};
}
Custom Authorization
const mastraAuth = new MastraAuthBetterAuth ({
auth ,
authorizeUser : async ({ session , user }) => {
// Check if email is verified
if ( ! user . emailVerified ) {
return false ;
}
// Check if session is expired
if ( new Date ( session . expiresAt ) < new Date ()) {
return false ;
}
// Check user role (if you have roles plugin)
// const hasRole = user.roles?.includes('admin');
return true ;
},
});
Database Setup
better-auth automatically creates required tables:
const auth = betterAuth ({
database: {
provider: 'postgresql' , // or 'mysql', 'sqlite'
url: process . env . DATABASE_URL ! ,
},
});
Tables created:
user - User accounts
session - Active sessions
account - Social provider accounts
verification - Email verification tokens
Plugins
Two-Factor Authentication
import { twoFactor } from 'better-auth/plugins' ;
const auth = betterAuth ({
database: {
provider: 'postgresql' ,
url: process . env . DATABASE_URL ! ,
},
plugins: [ twoFactor ()],
});
Organization/Multi-Tenancy
import { organization } from 'better-auth/plugins' ;
const auth = betterAuth ({
database: {
provider: 'postgresql' ,
url: process . env . DATABASE_URL ! ,
},
plugins: [ organization ()],
});
Best Practices
Self-Hosted Control You own the database and have full control over user data and authentication logic.
Enable Email Verification Require email verification for better security: requireEmailVerification : true
Use Environment Variables Store OAuth secrets and database URLs in environment variables.
Session Management Configure session expiration based on your security requirements.
Authentication Flow
Advantages
Self-Hosted Complete control over your authentication system and user data
Open Source Fully open-source with transparent implementation
Framework Agnostic Works with any JavaScript framework or runtime
Extensible Plugin system for adding custom functionality
Clerk Managed authentication alternative
Supabase Auth Open-source auth with database
better-auth Docs Official better-auth documentation
better-auth GitHub Open-source repository