Openclaw-master-skills better-auth-best-practices

Skill for integrating Better Auth - the comprehensive TypeScript authentication framework.

install
source · Clone the upstream repo
git clone https://github.com/LeoYeAI/openclaw-master-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/LeoYeAI/openclaw-master-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/better-auth-best-practices" ~/.claude/skills/leoyeai-openclaw-master-skills-better-auth-best-practices && rm -rf "$T"
OpenClaw · Install into ~/.openclaw/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/LeoYeAI/openclaw-master-skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/better-auth-best-practices" ~/.openclaw/skills/leoyeai-openclaw-master-skills-better-auth-best-practices && rm -rf "$T"
manifest: skills/better-auth-best-practices/SKILL.md
safety · automated scan (medium risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
  • dumps environment variables
  • references API keys
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content

Better Auth Integration Guide

Always consult better-auth.com/docs for code examples and latest API.

Better Auth is a TypeScript-first, framework-agnostic auth framework supporting email/password, OAuth, magic links, passkeys, and more via plugins.


Quick Reference

Environment Variables

  • BETTER_AUTH_SECRET
    - Encryption secret (min 32 chars). Generate:
    openssl rand -base64 32
  • BETTER_AUTH_URL
    - Base URL (e.g.,
    https://example.com
    )

Only define

baseURL
/
secret
in config if env vars are NOT set.

File Location

CLI looks for

auth.ts
in:
./
,
./lib
,
./utils
, or under
./src
. Use
--config
for custom path.

CLI Commands

  • npx @better-auth/cli@latest migrate
    - Apply schema (built-in adapter)
  • npx @better-auth/cli@latest generate
    - Generate schema for Prisma/Drizzle
  • npx @better-auth/cli mcp --cursor
    - Add MCP to AI tools

Re-run after adding/changing plugins.


Core Config Options

OptionNotes
appName
Optional display name
baseURL
Only if
BETTER_AUTH_URL
not set
basePath
Default
/api/auth
. Set
/
for root.
secret
Only if
BETTER_AUTH_SECRET
not set
database
Required for most features. See adapters docs.
secondaryStorage
Redis/KV for sessions & rate limits
emailAndPassword
{ enabled: true }
to activate
socialProviders
{ google: { clientId, clientSecret }, ... }
plugins
Array of plugins
trustedOrigins
CSRF whitelist

Database

Direct connections: Pass

pg.Pool
,
mysql2
pool,
better-sqlite3
, or
bun:sqlite
instance.

ORM adapters: Import from

better-auth/adapters/drizzle
,
better-auth/adapters/prisma
,
better-auth/adapters/mongodb
.

Critical: Better Auth uses adapter model names, NOT underlying table names. If Prisma model is

User
mapping to table
users
, use
modelName: "user"
(Prisma reference), not
"users"
.


Session Management

Storage priority:

  1. If
    secondaryStorage
    defined → sessions go there (not DB)
  2. Set
    session.storeSessionInDatabase: true
    to also persist to DB
  3. No database +
    cookieCache
    → fully stateless mode

Cookie cache strategies:

  • compact
    (default) - Base64url + HMAC. Smallest.
  • jwt
    - Standard JWT. Readable but signed.
  • jwe
    - Encrypted. Maximum security.

Key options:

session.expiresIn
(default 7 days),
session.updateAge
(refresh interval),
session.cookieCache.maxAge
,
session.cookieCache.version
(change to invalidate all sessions).


User & Account Config

User:

user.modelName
,
user.fields
(column mapping),
user.additionalFields
,
user.changeEmail.enabled
(disabled by default),
user.deleteUser.enabled
(disabled by default).

Account:

account.modelName
,
account.accountLinking.enabled
,
account.storeAccountCookie
(for stateless OAuth).

Required for registration:

email
and
name
fields.


Email Flows

  • emailVerification.sendVerificationEmail
    - Must be defined for verification to work
  • emailVerification.sendOnSignUp
    /
    sendOnSignIn
    - Auto-send triggers
  • emailAndPassword.sendResetPassword
    - Password reset email handler

Security

In

advanced
:

  • useSecureCookies
    - Force HTTPS cookies
  • disableCSRFCheck
    - ⚠️ Security risk
  • disableOriginCheck
    - ⚠️ Security risk
  • crossSubDomainCookies.enabled
    - Share cookies across subdomains
  • ipAddress.ipAddressHeaders
    - Custom IP headers for proxies
  • database.generateId
    - Custom ID generation or
    "serial"
    /
    "uuid"
    /
    false

Rate limiting:

rateLimit.enabled
,
rateLimit.window
,
rateLimit.max
,
rateLimit.storage
("memory" | "database" | "secondary-storage").


Hooks

Endpoint hooks:

hooks.before
/
hooks.after
- Array of
{ matcher, handler }
. Use
createAuthMiddleware
. Access
ctx.path
,
ctx.context.returned
(after),
ctx.context.session
.

Database hooks:

databaseHooks.user.create.before/after
, same for
session
,
account
. Useful for adding default values or post-creation actions.

Hook context (

ctx.context
):
session
,
secret
,
authCookies
,
password.hash()
/
verify()
,
adapter
,
internalAdapter
,
generateId()
,
tables
,
baseURL
.


Plugins

Import from dedicated paths for tree-shaking:

import { twoFactor } from "better-auth/plugins/two-factor"

NOT

from "better-auth/plugins"
.

Popular plugins:

twoFactor
,
organization
,
passkey
,
magicLink
,
emailOtp
,
username
,
phoneNumber
,
admin
,
apiKey
,
bearer
,
jwt
,
multiSession
,
sso
,
oauthProvider
,
oidcProvider
,
openAPI
,
genericOAuth
.

Client plugins go in

createAuthClient({ plugins: [...] })
.


Client

Import from:

better-auth/client
(vanilla),
better-auth/react
,
better-auth/vue
,
better-auth/svelte
,
better-auth/solid
.

Key methods:

signUp.email()
,
signIn.email()
,
signIn.social()
,
signOut()
,
useSession()
,
getSession()
,
revokeSession()
,
revokeSessions()
.


Type Safety

Infer types:

typeof auth.$Infer.Session
,
typeof auth.$Infer.Session.user
.

For separate client/server projects:

createAuthClient<typeof auth>()
.


Common Gotchas

  1. Model vs table name - Config uses ORM model name, not DB table name
  2. Plugin schema - Re-run CLI after adding plugins
  3. Secondary storage - Sessions go there by default, not DB
  4. Cookie cache - Custom session fields NOT cached, always re-fetched
  5. Stateless mode - No DB = session in cookie only, logout on cache expiry
  6. Change email flow - Sends to current email first, then new email

Resources