@xenterprises/nuxt-x-auth
v0.2.1
Published
Authentication layer for Nuxt with multi-provider support (Stack Auth, Better Auth, Local JWT)
Downloads
24
Readme
@xenterprises/nuxt-x-auth
Universal authentication layer for Nuxt 4 with multi-provider support. Drop-in auth pages, components, and composables that work with Stack Auth, Better Auth, Neon Auth, or a custom JWT backend.
Install
npm install @xenterprises/nuxt-x-authQuick Start
// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@xenterprises/nuxt-x-auth']
})// app.config.ts
export default defineAppConfig({
xAuth: {
provider: 'stack', // 'stack' | 'better-auth' | 'neon-auth' | 'local'
}
})<script setup>
const { user, login, logout, isAuthenticated } = useXAuth()
await login('[email protected]', 'password')
</script>Options (app.config.ts)
| Option | Type | Default | Required | Description |
|--------|------|---------|----------|-------------|
| provider | 'stack' \| 'better-auth' \| 'neon-auth' \| 'local' | 'stack' | Yes | Auth provider to use |
| tokens.accessCookie | string | 'x_auth_access' | No | Cookie name for access token |
| tokens.refreshCookie | string | 'x_auth_refresh' | No | Cookie name for refresh token |
| tokens.hasRefresh | boolean | true | No | Whether provider supports refresh tokens |
| redirects.login | string | '/auth/login' | No | Login page path |
| redirects.signup | string | '/auth/signup' | No | Signup page path |
| redirects.afterLogin | string | '/' | No | Redirect after login |
| redirects.afterSignup | string | '/' | No | Redirect after signup |
| redirects.afterLogout | string | '/auth/login' | No | Redirect after logout |
| redirects.forgotPassword | string | '/auth/forgot-password' | No | Forgot password path |
| features.oauth | boolean | false | No | Enable OAuth buttons |
| features.magicLink | boolean | false | No | Enable magic link auth |
| features.otp | boolean | false | No | Enable OTP auth |
| features.forgotPassword | boolean | true | No | Show forgot password link |
| features.signup | boolean | true | No | Show signup link |
| oauthProviders | Array<{id, label, icon}> | [] | No | OAuth providers to display |
| fieldMapping.id | string | provider-specific | No | Field name for user ID |
| fieldMapping.name | string | provider-specific | No | Field name for display name |
| fieldMapping.avatar | string | provider-specific | No | Field name for avatar URL |
| fieldMapping.emailVerified | string | provider-specific | No | Field name for email verified flag |
| ui.showLogo | boolean | true | No | Show logo on auth pages |
| ui.logoUrl | string | '' | No | Logo image URL |
| ui.brandName | string | '' | No | Brand name for alt text |
| ui.tagline | string | '' | No | Tagline below brand name |
| ui.layout | 'centered' \| 'split' | 'centered' | No | Auth page layout style |
| ui.form.icon | string | '' | No | Icon above form title |
| ui.form.showSeparator | boolean | true | No | Show "or" separator between OAuth and form |
Composable: useXAuth()
Auto-imported. Returns:
| Property | Type | Description |
|----------|------|-------------|
| user | Ref<AuthUser \| null> | Current authenticated user |
| isLoading | Ref<boolean> | Loading state |
| isAuthenticated | ComputedRef<boolean> | Whether user is logged in |
| emailSent | Ref<boolean> | Email sent state (forgot password, magic link) |
| codeSent | Ref<boolean> | Code sent state (OTP) |
| config | ComputedRef<AuthConfig> | Current auth config |
| providerType | string | Active provider type |
| login(email, password) | Promise<AuthUser \| null> | Login with credentials |
| signup(email, password) | Promise<AuthUser \| null> | Create account |
| logout() | Promise<boolean> | Sign out |
| forgotPassword(email) | Promise<boolean> | Send reset email |
| resetPassword(code, newPassword) | Promise<true \| {error}> | Reset password |
| loginWithProvider(name, options?) | Promise<boolean> | OAuth login |
| sendOtp(email) | Promise<boolean> | Send OTP code |
| verifyOtp(code) | Promise<AuthUser \| null> | Verify OTP |
| sendMagicLink(email, options?) | Promise<boolean> | Send magic link |
| handleMagicLinkCallback(code) | Promise<true \| {error}> | Handle magic link callback |
| getCurrentUser() | Promise<AuthUser \| null> | Refresh user from provider |
| getToken() | Promise<string \| null> | Get access token |
| getAuthHeaders() | Promise<Record<string, string>> | Get auth headers for API calls |
| resetState() | void | Reset emailSent/codeSent |
Normalized User Object
All providers return a normalized AuthUser:
interface AuthUser {
id: string
email: string
name: string
avatar?: string
emailVerified: boolean
metadata?: Record<string, any>
}Field mapping is automatic per provider with fallback chains. Override via fieldMapping in config.
Components
| Component | Description |
|-----------|-------------|
| XAuthForm | Base form component with fields, OAuth providers, and footer slot |
| XAuthLogin | Login form with email/password, OAuth, links to signup/forgot/OTP/magic |
| XAuthSignup | Signup form with email/password and OAuth |
| XAuthForgotPassword | Password reset request form with success state |
| XAuthOtp | Two-step OTP flow: email input, then code verification |
| XAuthMagicLink | Magic link request form with success state |
| XAuthHandler | Callback handler for OAuth, magic link, password reset, email verification |
| XAuthMagicLinkCallback | Standalone magic link callback handler |
| XAuthOAuthButton | Single OAuth provider button with inline SVG icons |
| XAuthOAuthButtonGroup | All configured OAuth buttons grouped |
Pages (Auto-registered)
| Path | Description | Access |
|------|-------------|--------|
| /auth/login | Login page | Guest only |
| /auth/signup | Registration page | Guest only |
| /auth/forgot-password | Password reset request | Guest only |
| /auth/otp | OTP authentication | Guest only |
| /auth/magic-link | Magic link authentication | Guest only |
| /auth/logout | Logout handler | Public |
| /auth/handler/[...slug] | OAuth/callback handler | Public |
Route Protection
The global middleware (auth.global.ts) automatically protects routes:
- Guest-only:
/auth/login,/auth/signup,/auth/forgot-password,/auth/magic-link,/auth/otp— authenticated users are redirected toafterLogin - Public:
/auth/handler/*,/auth/logout— accessible by anyone - Protected: Everything else — unauthenticated users are redirected to
login
Environment Variables
Stack Auth
| Variable | Required | Description |
|----------|----------|-------------|
| NUXT_PUBLIC_STACK_PROJECT_ID | Yes | Stack Auth project ID |
| NUXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY | Yes | Stack Auth publishable key |
Better Auth
| Variable | Required | Description |
|----------|----------|-------------|
| NUXT_PUBLIC_BETTER_AUTH_BASE_URL | Yes | Better Auth API base URL |
Neon Auth
| Variable | Required | Description |
|----------|----------|-------------|
| NUXT_PUBLIC_NEON_AUTH_PROJECT_ID | Yes | Neon project ID |
| NUXT_PUBLIC_NEON_AUTH_BRANCH_ID | No | Neon branch ID for branch-aware auth |
| NUXT_PUBLIC_NEON_AUTH_BASE_URL | No | Override base URL (default: https://{projectId}.auth.neon.tech) |
Local JWT
| Variable | Required | Description |
|----------|----------|-------------|
| NUXT_PUBLIC_LOCAL_AUTH_BASE_URL | Yes | API base URL |
| NUXT_PUBLIC_LOCAL_AUTH_LOGIN_ENDPOINT | No | Login endpoint (default: /auth/login) |
| NUXT_PUBLIC_LOCAL_AUTH_SIGNUP_ENDPOINT | No | Signup endpoint (default: /auth/signup) |
| NUXT_PUBLIC_LOCAL_AUTH_LOGOUT_ENDPOINT | No | Logout endpoint (default: /auth/logout) |
| NUXT_PUBLIC_LOCAL_AUTH_REFRESH_ENDPOINT | No | Token refresh endpoint (default: /auth/refresh) |
| NUXT_PUBLIC_LOCAL_AUTH_USER_ENDPOINT | No | User info endpoint (default: /auth/me) |
Providers
Stack Auth
Full-featured auth with OAuth, MFA, magic links, and OTP. Uses Stack's built-in cookie-based token storage. Supports all authentication methods.
Better Auth
Open-source auth framework. Self-hosted or cloud. Supports email/password, OAuth, and magic links. Uses cookie storage via this layer.
Neon Auth
Neon's managed Better Auth service. Adds branch-aware authentication via X-Neon-Branch-Id header. Same API as Better Auth with project-scoped endpoints.
Local JWT
Custom JWT backend. Fully configurable endpoints. Supports access + refresh tokens with automatic retry on 401. Uses cookie storage via this layer.
Token Storage
Tokens are stored in cookies with secure defaults:
SameSite=Lax— CSRF protectionSecureflag in production — HTTPS only- Access token: 1 hour expiry
- Refresh token: 7 day expiry
Cookie names are configurable via tokens.accessCookie and tokens.refreshCookie.
How It Works
Provider abstraction:
useXAuth()selects a provider based onapp.config.tsand delegates all auth operations to it. Each provider implements theAuthProviderinterface.Field normalization: Raw user objects from each provider are normalized to a unified
AuthUserformat usingnormalizeUser(). Provider-specific field names (e.g.displayNamevsname) are resolved via default mappings and configurable fallback chains.Cookie storage:
useCookieStorage()wraps Nuxt'suseCookieto manage access/refresh tokens with secure defaults. Used by Better Auth, Neon Auth, and Local JWT providers.Global middleware:
auth.global.tsruns on every navigation. It classifies routes as guest-only, public, or protected, then checks authentication state viagetCurrentUser()and redirects accordingly.Auth token plugin: Provides
$getAuthTokenvia Nuxt plugin so other layers (e.g.nuxt-x-app) can make authenticated API calls without coupling to a specific auth provider.
Layer Architecture
nuxt.config.ts: Registers@nuxt/ui, definesruntimeConfig.publicfor all provider credentials, includes base CSS.app.config.ts: Defines all configurable options (provider, tokens, redirects, features, UI). Consumer apps override these in their ownapp.config.ts.app/: Contains all components, composables, pages, layouts, middleware, plugins, and utilities.
Requirements
- Nuxt 4+
- @nuxt/ui v4+ (included as dependency)
better-auth(optional peer dependency, only needed for Better Auth provider)
License
UNLICENSED
