@mantleframework/auth
Better Auth integration for session-based authentication in Lambda handlers, with Drizzle ORM adapter, schema mapping, and permission exports for the CLI.
Setup
createAuth(db, config)
Create and cache a Better Auth instance with a Drizzle database adapter. Cached for Lambda container reuse.
import { createAuth } from '@mantleframework/auth'
import { getRequiredEnv } from '@mantleframework/env'
const auth = createAuth(db, {
secret: getRequiredEnv('AUTH_SECRET'),
baseURL: getRequiredEnv('AUTH_BASE_URL'),
socialProviders: {
apple: {
clientId: getRequiredEnv('APPLE_CLIENT_ID'),
clientSecret: getRequiredEnv('APPLE_CLIENT_SECRET'),
appBundleIdentifier: 'com.example.app',
},
google: {
clientId: getRequiredEnv('GOOGLE_CLIENT_ID'),
clientSecret: getRequiredEnv('GOOGLE_CLIENT_SECRET'),
},
},
})getAuth(getDb, config)
Lazily initialize auth with an async database factory function. Returns the cached instance on subsequent calls.
import { getAuth } from '@mantleframework/auth'
const auth = await getAuth(
() => getDrizzleClient({ provider: 'aurora-dsql', endpoint: '...' }),
{ secret: '...', baseURL: '...' },
)resetAuth()
Reset the cached auth instance so the next call to createAuth or getAuth creates a fresh one. Intended for test isolation.
Session Validation
validateSession(auth, token)
Validate a bearer token against Better Auth and return the authenticated user and session.
import { validateSession } from '@mantleframework/auth'
const result = await validateSession(auth, bearerToken)
// result.user.id, result.user.email, result.session.expiresAtThrows UnauthorizedError when the token is missing, invalid, or expired.
validateSessionFromHeader(auth, authorizationHeader)
Extract a bearer token from the Authorization header and validate it in one call.
const result = await validateSessionFromHeader(auth, event.headers.Authorization)Throws UnauthorizedError when the header is missing, malformed, or the token is invalid.
extractBearerToken(header)
Parse a bearer token from an Authorization header value. Returns null if the header is missing or malformed.
const token = extractBearerToken('Bearer abc123')
// 'abc123'expireSession(auth, token, db)
Expire a session by setting its expiresAt to now, preserving the row for audit and cleanup. Unlike Better Auth's revokeSession() which deletes the session row, this keeps it visible for scheduled cleanup.
import { expireSession } from '@mantleframework/auth'
await expireSession(auth, bearerToken, db)Throws UnauthorizedError when the token is invalid or already expired.
getSessionExpirationISO(session)
Return the session expiration as an ISO 8601 string.
const iso = getSessionExpirationISO(sessionResult)
// '2026-04-28T12:00:00.000Z'refreshSession(auth, token) :badge[deprecated]
Validate and refresh a session. Deprecated -- use validateSession instead. BetterAuth's getSession() auto-extends sessions when updateAge has elapsed, making a separate refresh function unnecessary.
Session Auth Configuration
One-time setup for defineApiHandler with auth: 'session'.
configureSessionAuth(getDb, config)
Register a session auth factory for use by defineApiHandler with auth: 'session'. Call once at module scope in your Lambda entry file, before any handler that uses session auth.
import { configureSessionAuth } from '@mantleframework/auth'
configureSessionAuth(
() => getDrizzleClient({ provider: 'aurora-dsql', endpoint: '...' }),
{ secret: getRequiredEnv('AUTH_SECRET'), baseURL: getRequiredEnv('AUTH_BASE_URL') },
)getConfiguredSessionAuth()
Return the configured Better Auth instance, resolving it via the registered factory. Throws if configureSessionAuth has not been called.
resetSessionAuth()
Reset the session auth factory. Intended for test isolation.
Permissions
AUTH_TABLE_PERMISSIONS
Table permissions required by Better Auth for session management. Consumed by mantle generate permissions to auto-generate DSQL role grants for Lambdas that import @mantleframework/auth.
const AUTH_TABLE_PERMISSIONS: TablePermission[] = [
{ table: 'users', operations: [Select, Insert, Update] },
{ table: 'sessions', operations: [Select, Insert, Update, Delete] },
{ table: 'accounts', operations: [Select, Insert, Delete] },
{ table: 'verification', operations: [Select, Insert, Delete] },
]AUTH_MIGRATION_SQL
SQL string to create all Better Auth tables and indexes. Idempotent (uses IF NOT EXISTS). Creates: users, sessions, accounts, verification tables with appropriate indexes.
Schema
Reusable Drizzle ORM column definitions and table schemas for Better Auth. Instances can spread these into their own pgTable() calls to extend auth tables with additional columns.
Column Definitions
import {
AUTH_USER_COLUMNS,
AUTH_SESSION_COLUMNS,
AUTH_ACCOUNT_COLUMNS,
AUTH_VERIFICATION_COLUMNS,
} from '@mantleframework/auth'Each is a Record<string, AnyPgColumn> that can be spread into pgTable() calls:
import { pgTable, text } from 'drizzle-orm/pg-core'
import { AUTH_USER_COLUMNS } from '@mantleframework/auth'
export const users = pgTable('users', {
...AUTH_USER_COLUMNS,
customField: text('custom_field'),
})Default Tables
Pre-built Drizzle tables using the standard column definitions:
import { authUsers, authSessions, authAccounts, authVerification } from '@mantleframework/auth'AUTH_SCHEMA_MAPPING
Schema mapping in the format expected by Better Auth's drizzleAdapter. Maps Better Auth model names to Drizzle tables:
const AUTH_SCHEMA_MAPPING = {
user: authUsers,
session: authSessions,
account: authAccounts,
verification: authVerification,
}Pass to drizzleAdapter({ schema: AUTH_SCHEMA_MAPPING }) or let createAuth() apply it automatically.
Types
AuthConfig
interface AuthConfig {
secret: string
baseURL: string
trustedOrigins?: string[]
sessionExpiresIn?: number // seconds, default: 30 days
sessionUpdateAge?: number // seconds, default: 24 hours
socialProviders?: {
apple?: SocialProviderConfig
google?: SocialProviderConfig
github?: SocialProviderConfig
custom?: CustomOAuthProvider[]
}
schema?: Record<string, unknown>
useSecureCookies?: boolean // default: true
databaseGenerateId?: boolean
encryptOAuthTokens?: boolean // default: false
experimentalJoins?: boolean // default: false (BetterAuth v1.5+)
}SocialProviderConfig
interface SocialProviderConfig {
clientId: string
clientSecret: string
enabled?: boolean
appBundleIdentifier?: string
}CustomOAuthProvider
interface CustomOAuthProvider {
providerId: string
clientId: string
clientSecret: string
enabled?: boolean
options?: Record<string, unknown>
}SessionResult
interface SessionResult {
user: UserDetails
session: { id: string; token: string; expiresAt: Date }
}UserDetails
interface UserDetails {
id: string
email: string
name?: string
image?: string
emailVerified: boolean
}BetterAuthInstance
Opaque return type of betterAuth() used throughout the auth package.