Migrating from nuxt-auth-utils
This guide covers migrating from nuxt-auth-utils to Nuxt Better Auth.
Why Migrate?
| 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 |
Key Differences
Session Architecture
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.
Composable API
// nuxt-auth-utils
const { user, loggedIn, ready, fetch, clear } = useUserSession()
// nuxt-better-auth
const { user, session, loggedIn, ready, signIn, signUp, signOut, fetchSession, client } = useUserSession()
OAuth Handling
// 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!
}
}
})
Migration Steps
Remove nuxt-auth-utils
npx nuxi module rm nuxt-auth-utils
You can also delete NUXT_SESSION_PASSWORD from your .env file.
Install Nuxt Better Auth
Follow the Installation Guide to set up the module, environment variables, database, and configuration files.
Migrate OAuth Providers
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/**.
Update Session Usage
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>
Migrate Route Protection
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: { only: 'user', redirectTo: '/login' } },
'/login': { auth: { only: 'guest', redirectTo: '/app' } },
'/admin/**': { auth: { user: { role: 'admin' } } }
},
auth: {
preserveRedirect: true,
redirectQueryKey: 'redirect'
}
})
Delete your custom auth middleware.
Migrate API Protection
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' }
})
API Reference
| 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 |
Special Cases
Password Hashing Migration
Step 0: Validate Hash Compatibility (Recommended)
Before doing any migration work, validate that a known test user's password hash verifies correctly in a staging environment.
If you used the default nuxt-auth-utils hashing settings, you can usually keep existing hashes without any migration.
If you customized hashing in nuxt-auth-utils or Better Auth, treat this as a migration and use one of the options below.
Option 1: Require Password Resets
The simplest approach: require all users to reset their passwords during migration.
- Set all user passwords to
nullin your database - Direct users to password reset flow on first login
- New passwords will use Argon2id
Option 2: Support Both Hash Formats (Custom Hashing)
Implement 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
})
Option 3: Gradual Migration (Custom Hashing)
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)
WebAuthn Migration
If you used WebAuthn with nuxt-auth-utils:
- Export your WebAuthn credentials from the old format
- Install the Better Auth WebAuthn plugin
- Import credentials to the new schema
Data Migration
Better Auth creates user, session, and account tables. After setup, run schema generation:
Troubleshooting
Sessions not persisting
Ensure NUXT_BETTER_AUTH_SECRET is set and database is configured.
OAuth redirect errors
Auto-detected on Vercel/Cloudflare/Netlify. For other platforms, set NUXT_PUBLIC_SITE_URL.
Type errors on user fields
Run type augmentation - see Type Augmentation.