X Enterprises

nuxt-x-auth

Provider-agnostic authentication Nuxt layer — unified useXAuth composable, 9 pre-built UI components, global route middleware, and support for Stack Auth, Better Auth, Neon Auth, and Local JWT backends.

nuxt-x-auth

Provider-agnostic authentication layer for Nuxt. Register once and configure a single provider in app.config.ts — all UI components, route guards, and the useXAuth composable work identically regardless of the backend. Supports Stack Auth, Better Auth, Neon Auth, and Local JWT out of the box.

Installation

npm install @xenterprises/nuxt-x-auth
// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@xenterprises/nuxt-x-auth'],
})

Configuration

All configuration lives in app.config.ts under the xAuth key.

// app.config.ts
export default defineAppConfig({
  xAuth: {
    provider: 'stack', // 'stack' | 'better-auth' | 'neon-auth' | 'local'

    redirects: {
      login: '/auth/login',
      signup: '/auth/signup',
      afterLogin: '/',
      afterSignup: '/',
      afterLogout: '/auth/login',
      forgotPassword: '/auth/forgot-password',
    },

    features: {
      oauth: false,
      magicLink: false,
      otp: false,
      forgotPassword: true,
      signup: true,
    },

    oauthProviders: [
      { id: 'google', label: 'Google', icon: 'i-logos-google-icon' },
      { id: 'github', label: 'GitHub', icon: 'i-logos-github-icon' },
    ],

    tokens: {
      accessCookie: 'x_auth_access',
      refreshCookie: 'x_auth_refresh',
      hasRefresh: true,
    },

    ui: {
      showLogo: true,
      logoUrl: '',
      brandName: 'My App',
      tagline: 'Welcome back',
      layout: 'centered', // 'centered' | 'split'
      background: {
        type: 'gradient', // 'gradient' | 'image' | 'solid'
        imageUrl: '',
        overlayOpacity: 50,
      },
      card: {
        glass: false,
        glassIntensity: 'medium', // 'subtle' | 'medium' | 'strong'
      },
      split: {
        heroPosition: 'left', // 'left' | 'right'
        heroImageUrl: '',
        headline: '',
        subheadline: '',
        features: [],
      },
      form: {
        icon: '',
        showSeparator: true,
      },
    },
  },
})

provider options

ValueBackend
"stack"Stack Auth (default)
"better-auth"Better Auth
"neon-auth"Neon Auth (Better Auth wrapper)
"local"Local JWT API

endpoints overrides (Local JWT / custom)

endpoints: {
  login: '/auth/login',
  signup: '/auth/signup',
  logout: '/auth/logout',
  refresh: '/auth/refresh',
  me: '/auth/me',
  forgotPassword: '/auth/forgot-password',
  resetPassword: '/auth/reset-password',
}

fieldMapping overrides

Normalize provider-specific fields to the unified AuthUser shape:

fieldMapping: {
  id: 'sub',           // default tries: id, sub, user_id
  name: 'full_name',   // default tries: displayName, name, full_name, fullName
  avatar: 'image',     // default tries: avatarUrl, image, profile_picture
  emailVerified: 'verified',
}

useXAuth Composable

const {
  // State
  user,            // Ref<AuthUser | null>
  isLoading,       // Ref<boolean>
  isAuthenticated, // ComputedRef<boolean>
  emailSent,       // Ref<boolean> — true after forgotPassword/sendMagicLink
  codeSent,        // Ref<boolean> — true after sendOtp

  // Core
  login,           // (email, password) => Promise<AuthUser | null>
  signup,          // (email, password) => Promise<AuthUser | null>
  logout,          // () => Promise<boolean>

  // Password
  forgotPassword,  // (email) => Promise<boolean>
  resetPassword,   // (code, newPassword) => Promise<true | { error }>

  // OAuth
  loginWithProvider, // (providerName, options?) => Promise<boolean>

  // OTP
  sendOtp,         // (email) => Promise<boolean>
  verifyOtp,       // (code) => Promise<AuthUser | null>

  // Magic Link
  sendMagicLink,           // (email, options?) => Promise<boolean>
  handleMagicLinkCallback, // (code) => Promise<true | { error }>

  // Utilities
  getCurrentUser,  // () => Promise<AuthUser | null>
  getToken,        // () => Promise<string | null>
  getAuthHeaders,  // () => Promise<Record<string, string>>
  resetState,      // () => void

  // Info
  providerType,    // AuthProviderType
  config,          // ComputedRef<AuthConfig>
} = useXAuth()

AuthUser shape

All providers normalize their user data to this structure:

{
  id: string
  email: string
  name: string
  avatar?: string
  emailVerified: boolean
  metadata?: Record<string, any>
}

Examples

// Login
const user = await login('user@example.com', 'password')

// OAuth
await loginWithProvider('google')

// OTP flow
await sendOtp('user@example.com')   // sends code
await verifyOtp('123456')           // signs in

// Magic link flow
await sendMagicLink('user@example.com')
// user clicks link → /auth/handler/magic-link-callback
// page calls:
await handleMagicLinkCallback(route.query.code)

// Get token for API calls
const headers = await getAuthHeaders()
// { Authorization: 'Bearer eyJ...' }

Pre-Built Pages

The layer registers these pages automatically:

RouteComponentDescription
/auth/loginXAuthLoginEmail/password + OAuth + magic link/OTP links
/auth/signupXAuthSignupRegistration form
/auth/forgot-passwordXAuthForgotPasswordPassword reset request
/auth/magic-linkXAuthMagicLinkMagic link request
/auth/otpXAuthOtpOTP verification
/auth/logoutCalls logout() and redirects
/auth/handler/[...slug]XAuthHandlerOAuth/magic link callbacks

Components

All components are auto-imported with the XAuth prefix.

<XAuthLogin />

Full login form — email/password, optional OAuth buttons, links to signup/forgot password.

<XAuthSignup />

Registration form — email/password with confirmation.

<XAuthForgotPassword />

Password reset request form. Shows success state after submission (emailSent becomes true).

Magic link request form. Shows success state after email is sent.

<XAuthMagicLinkCallback />

Handles the magic link callback URL. Reads the code query param and calls handleMagicLinkCallback.

<XAuthOtp />

Two-step OTP: email input → code verification. Uses sendOtp then verifyOtp.

<XAuthOAuthButton />

Single OAuth provider button.

PropTypeDescription
provider{ id, label, icon }Provider definition

<XAuthOAuthButtonGroup />

Renders all configured oauthProviders from app.config.ts as a button group.

<XAuthHandler />

Handles auth callbacks (OAuth redirects, magic link completions). Reads the route slug and dispatches to the appropriate callback handler.

<XAuthForm />

Low-level form container used internally by the page components. Provides the shared card/layout/branding UI shell.

Global Route Middleware

A global auth.global.ts middleware runs on every navigation:

  • Guest-only routes (/auth/login, /auth/signup, /auth/forgot-password, /auth/magic-link, /auth/otp) — authenticated users are redirected to redirects.afterLogin.
  • Public routes (/auth/handler/*, /auth/logout) — always accessible, middleware skips.
  • All other routes — unauthenticated users are redirected to redirects.login.

Layouts

LayoutDescription
authAuth pages layout (full-screen, no nav)
defaultDefault app layout

Environment Variables

Configure the active provider via runtimeConfig (or environment variables):

VariableProviderDescription
NUXT_PUBLIC_STACK_PROJECT_IDStack AuthStack project ID
NUXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEYStack AuthPublishable client key
NUXT_PUBLIC_BETTER_AUTH_BASE_URLBetter AuthBetter Auth API base URL
NUXT_PUBLIC_NEON_AUTH_PROJECT_IDNeon AuthNeon project ID
NUXT_PUBLIC_NEON_AUTH_BRANCH_IDNeon AuthNeon branch ID
NUXT_PUBLIC_NEON_AUTH_BASE_URLNeon AuthNeon Auth base URL
NUXT_PUBLIC_LOCAL_AUTH_BASE_URLLocal JWTYour API base URL

How It Works

useXAuth reads appConfig.xAuth.provider at call time and instantiates the matching provider adapter (useStackAuthProvider, useBetterAuthProvider, useNeonAuthProvider, or useLocalAuthProvider). Each adapter implements the AuthProvider interface — the same method signatures regardless of backend. useXAuth wraps every method with loading state, toast notifications (via useToast()), and post-action redirects derived from appConfig.xAuth.redirects. All UI components consume useXAuth internally so swapping providers requires only changing the provider value in app.config.ts. The auth plugin (auth-token.ts) attaches the active token to outgoing requests. Token storage uses cookies (names configurable via tokens.accessCookie / tokens.refreshCookie).

Copyright © 2026