This guide covers migrating from nuxt-auth-utils to Nuxt Better Auth.
| nuxt-auth-utils | Nuxt Better Auth |
|---|---|
| Session-only (cookie storage) | Database-backed user records |
| Manual OAuth handlers per provider | Declarative OAuth config |
| Generic session object | Full TypeScript inference from plugins |
| Limited plugin ecosystem | Rich plugins (admin, 2FA, passkeys, orgs) |
| Manual session management | Built-in signIn/signUp/signOut |
nuxt-auth-utils: Arbitrary data in encrypted cookies via NUXT_SESSION_PASSWORD. No database required.
Better Auth: Database stores users + sessions. Cookies contain session tokens referencing DB records.
// nuxt-auth-utils
const { user, loggedIn, ready, fetch, clear } = useUserSession()
// nuxt-better-auth
const { user, session, loggedIn, ready, signIn, signUp, signOut, fetchSession, client } = useUserSession()
// nuxt-auth-utils - Event handler per provider
// server/routes/auth/github.get.ts
export default defineOAuthGitHubEventHandler({
async onSuccess(event, { user }) {
await setUserSession(event, { user: { id: user.id, name: user.name } })
return sendRedirect(event, '/')
}
})
// nuxt-better-auth - Declarative config
// server/auth.config.ts
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
export default defineServerAuth({
socialProviders: {
github: {
clientId: process.env.NUXT_OAUTH_GITHUB_CLIENT_ID!,
clientSecret: process.env.NUXT_OAUTH_GITHUB_CLIENT_SECRET!
}
}
})
npx nuxi module rm nuxt-auth-utils
You can also delete NUXT_SESSION_PASSWORD from your .env file.
Follow the Installation Guide to set up the module, environment variables, database, and configuration files.
Before:
export default defineOAuthGitHubEventHandler({
config: { scope: ['user:email'] },
async onSuccess(event, { user }) {
await setUserSession(event, {
user: { id: user.id, name: user.name, email: user.email }
})
return sendRedirect(event, '/dashboard')
}
})
After:
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
export default defineServerAuth({
socialProviders: {
github: {
clientId: process.env.NUXT_OAUTH_GITHUB_CLIENT_ID!,
clientSecret: process.env.NUXT_OAUTH_GITHUB_CLIENT_SECRET!,
scope: ['user:email']
}
}
})
Delete your OAuth route files (server/routes/auth/*.ts) - Better Auth handles routes automatically at /api/auth/**.
Before:
<script setup>
const { user, loggedIn, fetch, clear } = useUserSession()
async function logout() {
await clear()
navigateTo('/login')
}
</script>
After:
<script setup>
const { user, loggedIn, signOut } = useUserSession()
async function logout() {
await signOut()
navigateTo('/login')
}
</script>
Before (custom middleware):
export default defineNuxtRouteMiddleware((to) => {
const { loggedIn } = useUserSession()
if (!loggedIn.value && to.path.startsWith('/app')) {
return navigateTo('/login')
}
})
After (route rules):
export default defineNuxtConfig({
routeRules: {
'/app/**': { auth: 'user' },
'/login': { auth: 'guest' },
'/admin/**': { auth: { user: { role: 'admin' } } }
},
auth: {
redirects: { login: '/login', guest: '/' }
}
})
Delete your custom auth middleware.
Before:
export default defineEventHandler(async (event) => {
const session = await requireUserSession(event)
return { userId: session.user.id }
})
After:
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event)
return { userId: user.id }
})
With field matching:
const { user } = await requireUserSession(event, {
user: { role: 'admin' }
})
| nuxt-auth-utils | nuxt-better-auth | Notes |
|---|---|---|
useUserSession().user | useUserSession().user | Now typed from config |
useUserSession().fetch() | useUserSession().fetchSession() | Renamed |
useUserSession().clear() | useUserSession().signOut() | Renamed + server call |
setUserSession(event, data) | N/A | Handled by Better Auth |
getUserSession(event) | getUserSession(event) | Returns { user, session } |
requireUserSession(event) | requireUserSession(event, opts?) | Supports field matching |
clearUserSession(event) | N/A | Use client signOut() |
defineOAuth*EventHandler | socialProviders config | Declarative |
The simplest approach: require all users to reset their passwords during migration.
null in your databaseImplement a custom password verifier that tries both formats:
import { scrypt, timingSafeEqual } from 'node:crypto'
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
async function verifyLegacyHash(password: string, hash: string): Promise<boolean> {
// Implement scrypt verification for legacy hashes
// Return true if password matches legacy hash
}
export default defineServerAuth({
// Custom verification logic
})
Verify with scrypt first, then re-hash with Argon2id on successful login:
// On successful legacy verification:
const newHash = await hashPassword(password) // Argon2id
await updateUserPassword(userId, newHash)
If you used WebAuthn with nuxt-auth-utils:
Better Auth creates user, session, and account tables. After setup, run schema generation:
Ensure NUXT_BETTER_AUTH_SECRET is set and database is configured.
Auto-detected on Vercel/Cloudflare/Netlify. For other platforms, set NUXT_PUBLIC_SITE_URL.
Run type augmentation - see Type Augmentation.