@xenterprises/nuxt-x-auth-local
v0.2.1
Published
Local JWT authentication layer for Nuxt with custom backend support
Readme
@xenterprises/nuxt-x-auth-local
Local JWT authentication layer for Nuxt 4 — works with any backend that speaks JWT.
Install
npm install @xenterprises/nuxt-x-auth-localQuick Start
// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@xenterprises/nuxt-x-auth-local']
})NUXT_PUBLIC_LOCAL_AUTH_BASE_URL=https://api.example.comThat's it. The layer provides login, signup, forgot-password, reset-password pages and a global auth middleware out of the box.
Environment Variables
| Name | Required | Default | Description |
|------|----------|---------|-------------|
| NUXT_PUBLIC_LOCAL_AUTH_BASE_URL | Yes | "" | Base URL of your backend API |
| NUXT_PUBLIC_LOCAL_AUTH_LOGIN_ENDPOINT | No | /auth/login | Login endpoint path |
| NUXT_PUBLIC_LOCAL_AUTH_SIGNUP_ENDPOINT | No | /auth/signup | Signup endpoint path |
| NUXT_PUBLIC_LOCAL_AUTH_LOGOUT_ENDPOINT | No | /auth/logout | Logout endpoint path |
| NUXT_PUBLIC_LOCAL_AUTH_REFRESH_ENDPOINT | No | /auth/refresh | Token refresh endpoint path |
| NUXT_PUBLIC_LOCAL_AUTH_USER_ENDPOINT | No | /auth/me | Current user endpoint path |
| NUXT_PUBLIC_LOCAL_AUTH_FORGOT_PASSWORD_ENDPOINT | No | /auth/forgot-password | Forgot password endpoint |
| NUXT_PUBLIC_LOCAL_AUTH_RESET_PASSWORD_ENDPOINT | No | /auth/reset-password | Reset password endpoint |
| NUXT_PUBLIC_LOCAL_AUTH_CHANGE_PASSWORD_ENDPOINT | No | /auth/change-password | Change password endpoint |
Configuration (app.config.ts)
export default defineAppConfig({
xAuth: {
tokens: {
accessCookie: 'x_auth_access', // Cookie name for access token
refreshCookie: 'x_auth_refresh', // Cookie name for refresh token
hasRefresh: true, // Enable refresh token flow
},
redirects: {
login: '/auth/login',
signup: '/auth/signup',
afterLogin: '/dashboard',
afterSignup: '/dashboard',
afterLogout: '/auth/login',
forgotPassword: '/auth/forgot-password',
resetPassword: '/auth/reset-password',
},
features: {
forgotPassword: true, // Show forgot password link
signup: true, // Show signup link on login
},
ui: {
showLogo: true,
logoUrl: '/logo.svg',
brandName: 'My App',
tagline: '',
layout: 'centered', // 'centered' | 'split'
background: {
type: 'gradient', // 'gradient' | 'image' | 'solid'
imageUrl: '',
overlayOpacity: 50,
},
card: {
glass: false,
glassIntensity: 'medium', // 'subtle' | 'medium' | 'strong'
},
split: {
heroPosition: 'left',
heroImageUrl: '',
headline: '',
subheadline: '',
features: [],
},
form: {
icon: '',
showSeparator: true,
},
},
},
})Composable: useXAuth()
Auto-imported. State is shared across all components via useState.
<script setup>
const {
user, // Ref<AuthUser | null>
isLoading, // Ref<boolean>
isAuthenticated, // ComputedRef<boolean>
emailSent, // Ref<boolean>
config, // ComputedRef<AuthConfig>
login, // (email, password) => Promise<AuthUser | null>
signup, // (email, password) => Promise<AuthUser | null>
logout, // () => Promise<true>
forgotPassword, // (email) => Promise<boolean>
resetPassword, // (token, newPassword) => Promise<true | { error: string }>
changePassword, // (currentPassword, newPassword) => Promise<true | { error: string }>
refreshToken, // () => Promise<AuthTokens | null>
getCurrentUser, // () => Promise<AuthUser | null>
getToken, // () => string | null
getAuthHeaders, // () => Record<string, string>
resetState, // () => void
} = useXAuth()
</script>Methods
| Method | Args | Returns | Description |
|--------|------|---------|-------------|
| login | email: string, password: string | Promise<AuthUser \| null> | Authenticates and redirects to afterLogin |
| signup | email: string, password: string | Promise<AuthUser \| null> | Creates account and redirects to afterSignup |
| logout | — | Promise<true> | Clears tokens and redirects to afterLogout |
| forgotPassword | email: string | Promise<boolean> | Sends reset email (always shows success to prevent enumeration) |
| resetPassword | token: string, newPassword: string | Promise<true \| { error }> | Resets password using email token |
| changePassword | currentPassword: string, newPassword: string | Promise<true \| { error }> | Changes password for authenticated user |
| refreshToken | — | Promise<AuthTokens \| null> | Manually refresh the access token |
| getCurrentUser | — | Promise<AuthUser \| null> | Fetches current user from API |
| getToken | — | string \| null | Returns current access token |
| getAuthHeaders | — | Record<string, string> | Returns { Authorization: 'Bearer ...' } or {} |
| resetState | — | void | Resets emailSent flag |
Components
| Component | Description |
|-----------|-------------|
| XAuthForm | Base form component with fields, validation, password toggle |
| XAuthLogin | Login form with email/password, signup link, forgot password link |
| XAuthSignup | Signup form with email/password, login link, terms footer |
| XAuthForgotPassword | Forgot password form with email sent confirmation state |
XAuthForm Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| icon | string | — | Lucide icon name for form header |
| title | string | — | Form title |
| description | string | — | Description text below title |
| fields | Array<{ name, type, label, placeholder, required }> | [] | Form fields |
| submit | { label: string } | { label: 'Continue' } | Submit button config |
| schema | ZodObject | — | Zod validation schema |
| loading | boolean | false | Loading state |
XAuthForm Slots
| Slot | Description |
|------|-------------|
| description | Custom description content |
| password-hint | Hint below password fields (e.g., forgot password link) |
| footer | Footer content below submit button |
XAuthForm Events
| Event | Payload | Description |
|-------|---------|-------------|
| submit | { data: Record<string, any> } | Emitted on valid form submission |
Pages (Auto-registered)
| Route | Description |
|-------|-------------|
| /auth/login | Login page |
| /auth/signup | Registration page |
| /auth/forgot-password | Forgot password request |
| /auth/reset-password?token=... | Password reset (requires token query param) |
| /auth/logout | Logout handler |
Route Protection
The global middleware (auth.global.ts) handles three route types:
- Guest-only:
/auth/login,/auth/signup,/auth/forgot-password,/auth/reset-password— redirects authenticated users toafterLogin - Public:
/auth/logout— accessible by anyone - Protected: Everything else — redirects unauthenticated users to
login
Token Storage
Tokens are stored in Nuxt cookies (useCookie):
SameSite=Laxfor CSRF protectionSecureflag in production (HTTPS only)- Access token: 1 hour max-age
- Refresh token: 7 day max-age
- Cookie names are configurable via
xAuth.tokens
User Normalization
The layer normalizes user objects from any backend shape. It supports these field name fallbacks:
| Field | Fallback chain |
|-------|---------------|
| id | id, sub, user_id, userId, _id |
| email | email, primaryEmail, emailAddress |
| name | name, displayName, full_name, fullName, username |
| avatar | avatar, avatarUrl, image, profile_picture, photo |
| emailVerified | emailVerified, email_verified, verified, isVerified |
| metadata | metadata, customData |
API Requirements
Your backend must implement these endpoints (paths are configurable):
POST /auth/login
// Request
{ "email": "[email protected]", "password": "password123" }
// Response
{ "accessToken": "jwt-token", "refreshToken": "refresh-token" }POST /auth/signup
// Request
{ "email": "[email protected]", "password": "password123" }
// Response
{ "accessToken": "jwt-token", "refreshToken": "refresh-token" }GET /auth/me
Headers: Authorization: Bearer <accessToken>
// Response
{ "id": "user-123", "email": "[email protected]", "name": "John Doe" }POST /auth/refresh
// Request
{ "refreshToken": "refresh-token" }
// Response
{ "accessToken": "new-token", "refreshToken": "new-refresh" }POST /auth/logout
Headers: Authorization: Bearer <accessToken>
POST /auth/forgot-password
// Request
{ "email": "[email protected]" }POST /auth/reset-password
// Request
{ "token": "reset-token", "password": "newPassword123" }POST /auth/change-password
Headers: Authorization: Bearer <accessToken>
// Request
{ "currentPassword": "old", "newPassword": "new" }Error Reference
All errors are shown via useToast() with appropriate titles:
| Context | Toast Title | When | |---------|-------------|------| | Login | "Sign In Failed" | Invalid credentials, network error, empty input | | Signup | "Sign Up Failed" | Email taken, network error, empty input | | Forgot Password | "Password Reset Failed" | Empty email | | Change Password | "Change Password Failed" | Missing input, API error |
Security
- Open redirect protection: All redirect paths are validated — only relative paths starting with
/are allowed. Protocol-relative (//evil.com) and absolute URLs (https://evil.com) are blocked. - Email enumeration prevention:
forgotPasswordalways shows success, even on API errors. - Auto-retry on 401: If an API call returns 401, the layer attempts a token refresh and retries the request once.
How It Works
- Plugin (
auth-token.ts): Provides$getAuthTokenfor other layers/plugins to access the current JWT. - Middleware (
auth.global.ts): Runs on every navigation. CallsgetCurrentUser()to check auth state. Routes are classified as guest-only, public, or protected. - Cookie Storage (
cookieStorage.ts): WrapsuseCookiewith typed access/refresh token management. - Field Mapper (
fieldMapper.ts): Normalizes any backend user shape to the standardAuthUserinterface using fallback field chains. - Composable (
useXAuth.ts): Central auth logic. UsesuseStatefor shared state across components. Handles login/signup/logout flows, token refresh with auto-retry, and password reset. - Components:
XAuthFormis the base form with field rendering and Zod validation.XAuthLogin,XAuthSignup,XAuthForgotPasswordcompose it with pre-configured fields.
Layer Architecture
nuxt.config.ts: Registers@nuxt/ui, sets runtime config defaults for all endpoint paths, disables SSR.app.config.ts: UI and behavior configuration (tokens, redirects, features, visual settings). Override in consumer app.app/: All auto-imported composables, components, pages, layouts, plugins, and utilities.
Requirements
- Nuxt 4+
- A backend API implementing JWT authentication
License
UNLICENSED
