This library is in early development. Expect breaking changes.
Guides

Migrating from nuxt-auth-utils

Step-by-step guide to migrate from nuxt-auth-utils to Nuxt Better Auth.

This guide covers migrating from nuxt-auth-utils to Nuxt Better Auth.

Significant migration: nuxt-auth-utils stores sessions in encrypted cookies. Better Auth uses database-backed sessions with typed users. Plan accordingly.
This module is a Nuxt wrapper around Better Auth. Most auth logic (OAuth providers, plugins, email/password, etc.) is configured via Better Auth. Refer to their docs for provider-specific setup.

Why Migrate?

nuxt-auth-utilsNuxt Better Auth
Session-only (cookie storage)Database-backed user records
Manual OAuth handlers per providerDeclarative OAuth config
Generic session objectFull TypeScript inference from plugins
Limited plugin ecosystemRich plugins (admin, 2FA, passkeys, orgs)
Manual session managementBuilt-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.

For OAuth-only apps, Database-less Mode stores sessions in JWE cookies similar to nuxt-auth-utils.

Migrate OAuth Providers

Before:

server/routes/auth/github.get.ts
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:

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!,
      scope: ['user:email']
    }
  }
})

Delete your OAuth route files (server/routes/auth/*.ts) - Better Auth handles routes automatically at /api/auth/**.

See Better Auth OAuth docs for all supported providers and configuration options.

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):

middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const { loggedIn } = useUserSession()
  if (!loggedIn.value && to.path.startsWith('/app')) {
    return navigateTo('/login')
  }
})

After (route rules):

nuxt.config.ts
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.

Migrate API Protection

Before:

server/api/protected.get.ts
export default defineEventHandler(async (event) => {
  const session = await requireUserSession(event)
  return { userId: session.user.id }
})

After:

server/api/protected.get.ts
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-utilsnuxt-better-authNotes
useUserSession().useruseUserSession().userNow typed from config
useUserSession().fetch()useUserSession().fetchSession()Renamed
useUserSession().clear()useUserSession().signOut()Renamed + server call
setUserSession(event, data)N/AHandled by Better Auth
getUserSession(event)getUserSession(event)Returns { user, session }
requireUserSession(event)requireUserSession(event, opts?)Supports field matching
clearUserSession(event)N/AUse client signOut()
defineOAuth*EventHandlersocialProviders configDeclarative

Special Cases

Password Hashing Migration

nuxt-auth-utils uses scrypt for password hashing. Better Auth uses Argon2id by default. Existing password hashes will not work without migration.

The simplest approach: require all users to reset their passwords during migration.

  1. Set all user passwords to null in your database
  2. Direct users to password reset flow on first login
  3. New passwords will use Argon2id

Option 2: Support Both Hash Formats

Implement a custom password verifier that tries both formats:

server/auth.config.ts
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

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:

  1. Export your WebAuthn credentials from the old format
  2. Install the Better Auth WebAuthn plugin
  3. Import credentials to the new schema
See Better Auth WebAuthn documentation for schema requirements.

Data Migration

Better Auth creates user, session, and account tables. After setup, run schema generation:

See Schema Generation to set up database tables.

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.