npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@icecat-studio/nuxt-auth

v0.1.0

Published

Modern authentication module for Nuxt 3+ with token-based auth, auto-refresh, SSR support, and smart tab coordination

Readme

@icecat-studio/nuxt-auth

npm version npm downloads License Nuxt

Modern authentication module for Nuxt 3+ with token-based auth, auto-refresh, SSR support, and smart tab coordination.

Features

  • 🔐 Token-based Authentication — JWT/Bearer token support out of the box
  • 🔄 Auto-refresh Tokens — Automatic token renewal with configurable intervals
  • 🖥️ SSR Ready — Full server-side rendering support with proper hydration
  • 🎯 Smart Tab Coordination — Prevents multiple tabs from refreshing simultaneously
  • ⏸️ Pause on Inactive — Automatically pauses refresh when tab is hidden
  • 🛡️ Auth Fetch — Composable with automatic auth headers and 401 retry
  • 🎨 Flexible Token Management — Client-managed, server-managed (httpOnly), or no refresh
  • 📦 Type-safe — Full TypeScript support with user type augmentation
  • 🚀 Composable API — Simple useAuth() and useAuthFetch() composables
  • 🔧 Route Middleware — Built-in auth middleware for route protection

Quick Setup

Install the module:

npm install @icecat-studio/nuxt-auth

Add it to nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['@icecat-studio/nuxt-auth'],

  auth: {
    baseUrl: '/api/auth',
    endpoints: {
      login: { path: '/login', method: 'post' },
      logout: { path: '/logout', method: 'post' },
      refresh: { path: '/refresh', method: 'post' },
      user: { path: '/user', method: 'get' },
    },
  },
})

That's it! You can now use @icecat-studio/nuxt-auth in your Nuxt app ✨

Usage

Login

<script setup lang="ts">
const auth = useAuth()

const login = async () => {
  try {
    await auth.login({
      email: '[email protected]',
      password: 'password',
    })
    // Redirects to home page automatically
  }
  catch (error) {
    console.error('Login failed:', error)
  }
}
</script>

You can control redirect behavior per call:

// Disable redirect
await auth.login(credentials, { redirect: false })

// Custom redirect path
await auth.login(credentials, { redirect: '/dashboard' })

// External redirect
await auth.login(credentials, { redirect: { url: 'https://app.example.com', external: true } })

Registration

<script setup lang="ts">
const auth = useAuth()

const register = async () => {
  try {
    await auth.register({
      name: 'John Doe',
      email: '[email protected]',
      password: 'password',
    })
    // If the API returns tokens — logs in automatically
    // Otherwise — redirects to login page
  }
  catch (error) {
    console.error('Registration failed:', error)
  }
}
</script>

Protect Routes

<script setup lang="ts">
definePageMeta({
  auth: true, // Requires authentication
})
</script>

For guest-only pages (login, register):

<script setup lang="ts">
definePageMeta({
  auth: 'guest', // Redirects authenticated users away
})
</script>

Public pages (opt out of global middleware):

<script setup lang="ts">
definePageMeta({
  auth: false, // Accessible to everyone
})
</script>

Access User Data

<script setup lang="ts">
const auth = useAuth()
</script>

<template>
  <div v-if="auth.loggedIn.value">
    <p>Welcome, {{ auth.user.value?.name }}!</p>
    <button @click="auth.logout()">Logout</button>
  </div>
</template>

Authenticated HTTP Requests

Use useAuthFetch() for requests that need authentication. It automatically injects auth headers and handles 401 responses:

<script setup lang="ts">
const authFetch = useAuthFetch()

const { data } = await useAsyncData(() => authFetch('/api/orders'))
</script>

useAuthFetch provides:

  • Automatic Authorization header injection
  • Proactive token refresh if the access token is missing but refresh is available
  • Automatic retry on 401 after a successful token refresh (client-side)
  • Cookie forwarding during SSR for server-managed tokens

Authentication Modes

The module supports three token management strategies:

Client-Managed Refresh Token

The refresh token is stored in a client-accessible cookie and sent in the request body during refresh.

export default defineNuxtConfig({
  auth: {
    refreshToken: {
      serverManaged: false, // default
      property: 'refreshToken',
      bodyProperty: 'refreshToken',
    },
  },
})

Server-Managed Refresh Token (httpOnly)

The refresh token is managed entirely by the server via an httpOnly cookie. It is never accessible to JavaScript — the browser sends it automatically.

export default defineNuxtConfig({
  auth: {
    refreshToken: {
      serverManaged: true,
    },
  },
})

No Refresh (Access Token Only)

Disable the refresh endpoint for simple access-token-only flows. The session ends when the token expires.

export default defineNuxtConfig({
  auth: {
    endpoints: {
      refresh: false,
    },
  },
})

Configuration

Endpoints

Each endpoint can be configured or disabled individually:

export default defineNuxtConfig({
  auth: {
    baseUrl: '/api/auth',
    endpoints: {
      login: { path: '/login', method: 'post' },
      logout: { path: '/logout', method: 'post' },   // set to `false` to disable
      register: { path: '/register', method: 'post' }, // set to `false` to disable
      refresh: { path: '/refresh', method: 'post' },   // set to `false` to disable
      user: { path: '/user', method: 'get' },
    },
  },
})

You can also pass additional fetch options per endpoint:

endpoints: {
  login: {
    path: '/login',
    method: 'post',
    fetchOptions: { credentials: 'include' },
  },
}

Access Token

export default defineNuxtConfig({
  auth: {
    accessToken: {
      property: 'accessToken',      // Property in API response
      cookieName: 'auth.access_token',
      type: 'Bearer',               // Token type prefix
      headerName: 'Authorization',   // Header name
      maxAge: 900,                   // 15 minutes (in seconds)
      httpOnly: false,
      secure: true,
      sameSite: 'lax',
      path: '/',
    },
  },
})

Refresh Token

export default defineNuxtConfig({
  auth: {
    refreshToken: {
      property: 'refreshToken',        // Property in API response
      cookieName: 'auth.refresh_token',
      serverManaged: false,            // true = httpOnly cookie managed by server
      bodyProperty: 'refreshToken',    // Key name in refresh request body
      maxAge: 604800,                  // 7 days (in seconds)
      httpOnly: false,
      secure: true,
      sameSite: 'lax',
      path: '/',
    },
  },
})

Auto-Refresh

export default defineNuxtConfig({
  auth: {
    autoRefresh: {
      enabled: true,
      interval: 840,                  // 14 minutes (in seconds)
      pauseOnInactive: true,          // Pause when tab is hidden
      enableTabCoordination: true,    // Coordinate between tabs
      coordinationCookieName: 'auth.last_refresh',
      coordinationThreshold: 5,       // 5 seconds — skip if another tab refreshed within this time
    },
  },
})

Redirects

export default defineNuxtConfig({
  auth: {
    redirect: {
      login: '/login',      // Where to redirect unauthenticated users
      logout: '/',           // Where to redirect after logout
      home: '/',             // Where to redirect after login
    },
  },
})

Redirects also support external URLs:

redirect: {
  home: { url: 'https://app.example.com/dashboard', external: true },
}

User

export default defineNuxtConfig({
  auth: {
    user: {
      property: undefined,  // Nested property path (e.g., 'data.user'), undefined = entire response
      autoFetch: true,       // Automatically fetch user after login/refresh
    },
  },
})

Global Middleware

Enable authentication check on all routes by default:

export default defineNuxtConfig({
  auth: {
    globalMiddleware: true,
  },
})

Then opt out specific routes with auth: false in definePageMeta.

API Reference

useAuth<T>()

The main composable for authentication. Returns a singleton instance.

State

| Property | Type | Description | |---|---|---| | user | Ref<T \| null> | Current user data (reactive) | | status | Ref<AuthStatus> | Auth status (see below) | | loggedIn | ComputedRef<boolean> | Whether the user is logged in | | accessToken | Ref<string \| null> | Current access token | | refreshToken | Ref<string \| null> | Current refresh token (null if server-managed) | | canRefresh | ComputedRef<boolean> | Whether token refresh is available |

Auth status values:

| Status | Description | |---|---| | idle | Initial state, no auth activity | | loading | Login or registration in progress | | refreshing | Token refresh in progress | | authenticated | User is authenticated | | unauthenticated | No valid session |

loggedIn logic:

  • With refresh endpoint: true when status is authenticated or refreshing (session is alive until refresh explicitly fails)
  • Without refresh endpoint: true when status is authenticated and access token exists

Methods

| Method | Signature | Description | |---|---|---| | login | (credentials: Record<string, any>, options?: LoginOptions) => Promise<void> | Login with credentials | | logout | () => Promise<void> | Logout and clear session | | register | (data: Record<string, any>) => Promise<void> | Register a new user | | refresh | () => Promise<void> | Manually refresh tokens | | fetchUser | () => Promise<void> | Fetch user data from the user endpoint | | getAuthHeaders | () => Record<string, string> | Get authorization headers for manual requests |

useAuthFetch()

Returns an enhanced $fetch function with automatic auth handling:

const authFetch = useAuthFetch()

// Typed response
const user = await authFetch<User>('/api/me')

// With options
const data = await authFetch('/api/data', { method: 'POST', body: { key: 'value' } })

Behavior:

  1. Injects Authorization header with the current access token
  2. If no access token but refresh is available — refreshes first, then makes the request
  3. On 401 response (client-side only) — refreshes the token and retries the request once
  4. During SSR — forwards incoming request cookies for server-managed tokens

Advanced Usage

Custom API Calls with Auth Headers

If you need auth headers without using useAuthFetch:

const auth = useAuth()

const data = await $fetch('/api/protected', {
  headers: auth.getAuthHeaders(),
})

Manual Token Refresh

const auth = useAuth()

try {
  await auth.refresh()
  console.log('Token refreshed successfully')
}
catch (error) {
  console.error('Refresh failed:', error)
}

Controlling the Refresh Manager

The auto-refresh scheduler is exposed via useNuxtApp().$refreshManager:

const { $refreshManager } = useNuxtApp()

$refreshManager?.stop()    // Stop auto-refresh
$refreshManager?.start()   // Start auto-refresh
$refreshManager?.refresh() // Trigger immediate refresh

TypeScript: Augmenting the User Type

Define your user interface globally:

// types/auth.d.ts
declare module '@icecat-studio/nuxt-auth' {
  interface User {
    id: number
    email: string
    name: string
    avatar?: string
  }
}

Then useAuth() will infer the correct type:

const auth = useAuth()
auth.user.value?.name // string
auth.user.value?.id   // number

Or pass the type parameter directly:

interface MyUser {
  id: number
  name: string
}

const auth = useAuth<MyUser>()

How It Works

Session Initialization

  1. On app start, checks for existing tokens in cookies
  2. SSR: Attempts token refresh on the server; stores the result for the client
  3. Client hydration: If the server already refreshed, skips duplicate refresh and fetches user data
  4. Client-only navigation: Refreshes tokens and restores session normally

Auto-Refresh

  1. Schedules token refresh at the configured interval (default: 14 minutes)
  2. Pauses when the tab is hidden (if pauseOnInactive enabled)
  3. Resumes with an immediate refresh when the tab becomes visible
  4. On network error — reschedules with a doubled interval
  5. On server rejection (4xx) — stops auto-refresh and clears session

Tab Coordination

  1. Uses a shared cookie to track the last refresh timestamp
  2. Before refreshing, checks if another tab refreshed recently (within coordinationThreshold)
  3. Skips refresh if within the threshold, reschedules normal interval
  4. Updates the timestamp after a successful refresh

Error Handling

| Scenario | Behavior | |---|---| | Server rejection (4xx) | Clears tokens, sets status to unauthenticated, stops auto-refresh | | Network error | Keeps tokens intact, reverts status, reschedules with doubled interval | | 401 in useAuthFetch | Refreshes token and retries request once (client-only) | | Middleware: protected page, no session | Attempts refresh; redirects to login on failure |

Default Configuration

Full configuration with all default values:

export default defineNuxtConfig({
  auth: {
    baseUrl: '/api/auth',
    endpoints: {
      login:    { path: '/login',    method: 'post' },
      logout:   { path: '/logout',   method: 'post' },
      register: { path: '/register', method: 'post' },
      refresh:  { path: '/refresh',  method: 'post' },
      user:     { path: '/user',     method: 'get' },
    },
    accessToken: {
      property: 'accessToken',
      cookieName: 'auth.access_token',
      httpOnly: false,
      secure: true,
      sameSite: 'lax',
      path: '/',
      maxAge: 900,                  // 15 minutes
      type: 'Bearer',
      headerName: 'Authorization',
    },
    refreshToken: {
      property: 'refreshToken',
      cookieName: 'auth.refresh_token',
      httpOnly: false,
      secure: true,
      sameSite: 'lax',
      path: '/',
      maxAge: 604800,               // 7 days
      serverManaged: false,
      bodyProperty: 'refreshToken',
    },
    autoRefresh: {
      enabled: true,
      interval: 840,                // 14 minutes
      pauseOnInactive: true,
      enableTabCoordination: true,
      coordinationCookieName: 'auth.last_refresh',
      coordinationThreshold: 5,     // 5 seconds
    },
    user: {
      property: undefined,
      autoFetch: true,
    },
    redirect: {
      login: '/login',
      logout: '/',
      home: '/',
    },
    globalMiddleware: false,
  },
})

Contribution

# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with client-managed mode
npm run dev:client

# Develop with server-managed mode
npm run dev:server

# Develop without token refresh
npm run dev:no-refresh

# Build the playground
npm run dev:build

# Run ESLint
npm run lint

# Run Vitest
npm run test
npm run test:watch

# Run type checking
npm run test:types

# Release new version
npm run release

License

MIT License