Auth Package
Authentication integration with Better-auth using the OpenSaaS plugin system.
Installation
pnpm add @opensaas/stack-auth
Quick Start
Add the auth plugin to your OpenSaaS config:
// opensaas.config.ts
import { config, list, text, relationship } from '@opensaas/stack-core'
import { authPlugin } from '@opensaas/stack-auth'
export default config({
plugins: [
authPlugin({
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
},
sessionFields: ['userId', 'email', 'name'],
}),
],
db: {
provider: 'sqlite',
url: 'file:./dev.db',
},
lists: {
Post: list({
fields: {
title: text(),
author: relationship({ ref: 'User.posts' }),
},
access: {
operation: {
create: ({ session }) => !!session,
update: ({ session, item }) => session?.userId === item.authorId,
},
},
}),
},
})
Then set up the server and client:
// lib/auth.ts
import { createAuth } from '@opensaas/stack-auth/server'
import config from '../opensaas.config'
import { rawOpensaasContext } from '@/.opensaas/context'
export const auth = createAuth(config, rawOpensaasContext)
export const GET = auth.handler
export const POST = auth.handler
// lib/auth-client.ts
'use client'
import { createClient } from '@opensaas/stack-auth/client'
export const authClient = createClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
})
Configuration Options
The authPlugin() function accepts the following configuration options:
emailAndPassword
Configure email and password authentication:
authPlugin({
emailAndPassword: {
enabled: true,
minPasswordLength: 8, // default: 8
requireConfirmation: true, // default: true
},
})
emailVerification
Configure email verification for new sign-ups:
authPlugin({
emailVerification: {
enabled: true,
sendOnSignUp: true, // default: true
tokenExpiration: 86400, // default: 86400 (24 hours)
},
})
passwordReset
Configure password reset functionality:
authPlugin({
passwordReset: {
enabled: true,
tokenExpiration: 3600, // default: 3600 (1 hour)
},
})
socialProviders
Configure OAuth/social authentication providers:
authPlugin({
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
enabled: true,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
enabled: true,
},
discord: {
clientId: process.env.DISCORD_CLIENT_ID!,
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
},
},
})
Supported providers: github, google, discord, twitter
session
Configure session behavior:
authPlugin({
session: {
expiresIn: 604800, // default: 604800 (7 days)
updateAge: true, // default: true - update expiry on each request
},
})
sessionFields
Define which fields are available in the session object passed to access control functions:
authPlugin({
sessionFields: ['userId', 'email', 'name', 'role'],
})
These fields will be automatically typed and available in your access control functions:
access: {
operation: {
update: ({ session }) => {
// session is typed as { userId: string; email: string; name: string; role: string } | null
return session?.role === 'admin'
},
},
}
extendUserList
Add custom fields, access control, or hooks to the auto-generated User list:
authPlugin({
extendUserList: {
fields: {
role: select({
options: [
{ label: 'Admin', value: 'admin' },
{ label: 'User', value: 'user' },
],
defaultValue: 'user',
}),
posts: relationship({
ref: 'Post.author',
many: true,
}),
},
access: {
operation: {
delete: ({ session }) => session?.role === 'admin',
},
},
hooks: {
afterOperation: async ({ operation, item }) => {
if (operation === 'create') {
console.log('New user created:', item.email)
}
},
},
},
})
sendEmail
Provide a custom email sending function for verification and password reset emails:
authPlugin({
sendEmail: async ({ to, subject, html }) => {
// Use your email service (SendGrid, Resend, etc.)
await emailService.send({ to, subject, html })
},
})
If not provided, emails will be logged to the console in development.
betterAuthPlugins
Add Better Auth plugins for additional functionality:
import { authPlugin } from '@opensaas/stack-auth'
import { mcp } from '@opensaas/stack-auth/plugins'
authPlugin({
betterAuthPlugins: [
mcp({ loginPage: '/sign-in' }),
// Add other Better Auth plugins here
],
})
The auth plugin automatically converts Better Auth plugin schemas to OpenSaaS lists.
Auto-Generated Lists
The auth plugin automatically generates the following lists:
User
id(String, auto-generated)email(String, unique, required)emailVerified(Boolean)name(String, optional)image(String, optional)createdAt(DateTime, auto)updatedAt(DateTime, auto)- Custom fields from
extendUserList
Session
id(String, auto-generated)userId(String, foreign key to User)expiresAt(DateTime)token(String, unique)ipAddress(String, optional)userAgent(String, optional)createdAt(DateTime, auto)updatedAt(DateTime, auto)
Account
Stores OAuth provider information and password hashes:
id(String, auto-generated)userId(String, foreign key to User)accountId(String, provider-specific user ID)providerId(String, e.g., 'github', 'google')accessToken(String, optional)refreshToken(String, optional)expiresAt(DateTime, optional)password(String, optional, hashed)createdAt(DateTime, auto)updatedAt(DateTime, auto)
Verification
Stores email verification and password reset tokens:
id(String, auto-generated)identifier(String, email address)value(String, token)expiresAt(DateTime)createdAt(DateTime, auto)updatedAt(DateTime, auto)
Server Setup
Create auth handlers for your API routes:
// lib/auth.ts
import { createAuth } from '@opensaas/stack-auth/server'
import config from '../opensaas.config'
import { rawOpensaasContext } from '@/.opensaas/context'
export const auth = createAuth(config, rawOpensaasContext)
// Export handlers for Next.js API routes
export const GET = auth.handler
export const POST = auth.handler
Then create the API route:
// app/api/auth/[...all]/route.ts
export { GET, POST } from '@/lib/auth'
Client Setup
Create a client for authentication in your components:
// lib/auth-client.ts
'use client'
import { createClient } from '@opensaas/stack-auth/client'
export const authClient = createClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
})
export const {
signIn,
signUp,
signOut,
useSession,
// ... other auth methods
} = authClient
UI Components
The auth package includes pre-built UI components:
SignInForm
import { SignInForm } from '@opensaas/stack-auth/ui'
import { authClient } from '@/lib/auth-client'
export default function SignInPage() {
return (
<SignInForm
authClient={authClient}
redirectTo="/admin"
showSocialProviders={true}
/>
)
}
SignUpForm
import { SignUpForm } from '@opensaas/stack-auth/ui'
import { authClient } from '@/lib/auth-client'
export default function SignUpPage() {
return (
<SignUpForm
authClient={authClient}
redirectTo="/admin"
showSocialProviders={true}
/>
)
}
useSession Hook
'use client'
import { useSession } from '@/lib/auth-client'
export function UserProfile() {
const { data: session, isPending } = useSession()
if (isPending) return <div>Loading...</div>
if (!session) return <div>Not signed in</div>
return <div>Welcome, {session.user.name}!</div>
}
Access Control Integration
The session is automatically available in all access control functions:
lists: {
Post: list({
fields: {
title: text(),
content: text(),
author: relationship({ ref: 'User.posts' }),
},
access: {
operation: {
// Only authenticated users can create posts
create: ({ session }) => !!session,
// Only the author can update their posts
update: ({ session, item }) => {
return session?.userId === item.authorId
},
// Everyone can read published posts
query: () => true,
},
filter: {
// Users can only see their own drafts
query: ({ session }) => {
if (!session) {
return { status: { equals: 'published' } }
}
return {
OR: [
{ status: { equals: 'published' } },
{ authorId: { equals: session.userId } },
],
}
},
},
},
}),
}
MCP Integration
To enable Model Context Protocol support with Better Auth authentication:
import { authPlugin } from '@opensaas/stack-auth'
import { mcp } from '@opensaas/stack-auth/plugins'
export default config({
plugins: [
authPlugin({
emailAndPassword: { enabled: true },
betterAuthPlugins: [mcp({ loginPage: '/sign-in' })],
}),
],
mcp: {
enabled: true,
auth: {
type: 'better-auth',
loginPage: '/sign-in',
},
},
lists: {
// Your lists
},
})
The MCP plugin automatically converts its schema to OpenSaaS lists and enables OAuth authentication for AI assistants.
See MCP Integration Guide for more details.
Examples
- Basic Authentication - Email/password and OAuth
- MCP Integration - Better Auth MCP plugin
Further Reading
- Authentication Guide - Comprehensive authentication guide
- Access Control Guide - Using sessions in access control
- Better Auth Documentation - Official Better Auth docs