@habityzer/nuxt-kinde-auth

Kinde authentication module for Nuxt 4
2,675
Install
npm install @habityzer/nuxt-kinde-auth
Latest Version:1.4.5
Vue:3
License:MIT
Last Updated:Oct 22, 2026
Links: GitHub  ·  npm
Maintainer: vazgen

@habityzer/nuxt-kinde-auth

npm version npm downloads

Kinde authentication module for Nuxt 4

A plug-and-play authentication module for Nuxt 4 that integrates Kinde OAuth authentication with minimal setup.

Features

✅ Easy Setup - Add authentication to your Nuxt 4 app in minutes
✅ OAuth 2.0 - Secure authentication flow via Kinde
✅ Session Management - Cookie-based sessions with configurable options
✅ Route Protection - Optional global middleware to protect routes
✅ TypeScript - Full TypeScript support with type definitions
✅ Server-Side Ready - SSR-compatible authentication
✅ Debug Tools - Built-in debug endpoints for development

Installation

# Using pnpm (recommended)
pnpm add @habityzer/nuxt-kinde-auth

# Using npm
npm install @habityzer/nuxt-kinde-auth

# Using yarn
yarn add @habityzer/nuxt-kinde-auth

Quick Start

1. Add Module to Nuxt Config

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@habityzer/nuxt-kinde-auth'],
  
  kindeAuth: {
    authDomain: process.env.NUXT_KINDE_AUTH_DOMAIN,
    clientId: process.env.NUXT_KINDE_CLIENT_ID,
    clientSecret: process.env.NUXT_KINDE_CLIENT_SECRET,
    redirectURL: process.env.NUXT_KINDE_REDIRECT_URL,
    logoutRedirectURL: '/',
    postLoginRedirectURL: '/dashboard'
  }
})

2. Set Environment Variables

# .env
NUXT_KINDE_AUTH_DOMAIN=https://yourapp.kinde.com
NUXT_KINDE_CLIENT_ID=your_client_id
NUXT_KINDE_CLIENT_SECRET=your_client_secret
NUXT_KINDE_REDIRECT_URL=http://localhost:3000/api/kinde/callback
NUXT_KINDE_LOGOUT_REDIRECT_URL=http://localhost:3000

3. Use the Composable

<script setup lang="ts">
const { isAuthenticated, user, login, logout } = useKindeAuth()
</script>

<template>
  <div>
    <button v-if="!isAuthenticated" @click="login">
      Log in
    </button>
    
    <div v-else>
      <p>Welcome, {{ user?.given_name }}!</p>
      <button @click="logout">Log out</button>
    </div>
  </div>
</template>

Configuration

Full Configuration Options

export default defineNuxtConfig({
  kindeAuth: {
    // Required: Kinde domain
    authDomain: 'https://yourapp.kinde.com',
    
    // Required: Client credentials
    clientId: 'your_client_id',
    clientSecret: 'your_client_secret',
    
    // OAuth URLs
    redirectURL: '/api/kinde/callback',
    logoutRedirectURL: '/',
    postLoginRedirectURL: '/dashboard',
    
    // Cookie configuration
    cookie: {
      prefix: 'myapp_',    // Optional: Prefix for cookie names
      httpOnly: false,     // Allow client-side access for logout
      secure: true,        // HTTPS only in production
      sameSite: 'lax',     // CSRF protection
      path: '/',           // Cookie path
      maxAge: 604800       // 7 days in seconds
    },
    
    // Middleware configuration
    middleware: {
      enabled: true,                    // Enable route protection
      global: true,                     // Protect all routes by default
      publicRoutes: ['/', '/about']     // Routes that don't require auth
    },
    
    // Debug configuration
    debug: {
      enabled: false  // Enable debug endpoints in development only
    }
  }
})
Option Type Default Description
prefix string '' Cookie name prefix to prevent conflicts between projects
httpOnly boolean false HttpOnly flag for cookies
secure boolean true (prod) Secure flag for cookies
sameSite 'lax' | 'strict' | 'none' 'lax' SameSite attribute
path string '/' Cookie path
maxAge number 604800 Max age in seconds (7 days)

Note: The prefix option is useful when running multiple projects on localhost:3000. For example, setting prefix: 'project1_' will create cookies like project1_access_token instead of just access_token, preventing conflicts between different projects.

Middleware Options

Option Type Default Description
enabled boolean true Enable global middleware
global boolean true Make middleware global
publicRoutes string[] ['/'] Public routes (no auth required)

Composable API

useKindeAuth()

The main composable for authentication functionality.

Returns
{
  // State
  isAuthenticated: Ref<boolean>
  user: Ref<KindeUser | null>
  isLoading: Ref<boolean>
  
  // Computed
  userDisplayName: Ref<string>
  userEmail: Ref<string | null>
  userPicture: Ref<string | null>
  
  // Methods
  login: () => Promise<void>
  logout: () => void
  checkAuth: () => boolean
  fetchUser: () => Promise<KindeUser | null>
  getToken: () => Promise<string | null>
  refreshToken: () => Promise<boolean>
}
User Type
interface KindeUser {
  id: string
  email?: string
  given_name?: string
  family_name?: string
  picture?: string
  [key: string]: https://github.com/Habityzer/nuxt-kinde-auth/blob/HEAD/unknown
}

API Endpoints

The module automatically registers these endpoints:

Endpoint Method Description
/api/kinde/login GET Initiate OAuth login
/api/kinde/logout GET Logout and clear session
/api/kinde/callback GET OAuth callback handler
/api/kinde/user GET Get current user info
/api/kinde/token GET Get access token (auto-refreshes if expired)
/api/kinde/refresh GET Manually refresh access token
/api/kinde/debug/token GET Debug endpoint (dev only)

Server-Side Usage

Access Kinde context in server routes:

// server/api/example.ts
export default defineEventHandler(async (event) => {
  const kindeClient = event.context.kinde
  
  // Check authentication
  const isAuthenticated = await kindeClient.isAuthenticated()
  
  if (!isAuthenticated) {
    throw createError({
      statusCode: 401,
      statusMessage: 'Unauthorized'
    })
  }
  
  // Get user
  const user = await kindeClient.getUser()
  
  // Get token
  const token = await kindeClient.getToken()
  
  // Get permissions
  const permissions = await kindeClient.getPermissions()
  
  // Get feature flags
  const hasFeature = await kindeClient.getBooleanFlag('new_feature', false)
  
  return { user, permissions, hasFeature }
})

Route Protection

Using Built-in Middleware

The module provides optional global middleware for route protection:

// nuxt.config.ts
export default defineNuxtConfig({
  kindeAuth: {
    middleware: {
      enabled: true,
      global: true,
      publicRoutes: [
        '/',
        '/about',
        '/blog'
      ]
    }
  }
})

Custom Middleware

You can also create custom middleware:

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const { isAuthenticated, checkAuth } = useKindeAuth()
  
  checkAuth()
  
  if (!isAuthenticated.value && to.path.startsWith('/dashboard')) {
    return navigateTo('/api/kinde/login', { external: true })
  }
})

Advanced Usage

When developing multiple Nuxt projects locally, they typically run on the same domain (localhost:3000), which can cause cookie conflicts. Use the cookie.prefix option to isolate sessions:

// Project 1: Habityzer
export default defineNuxtConfig({
  kindeAuth: {
    cookie: {
      prefix: 'habityzer_'
    }
  }
})

// Project 2: Dashboard
export default defineNuxtConfig({
  kindeAuth: {
    cookie: {
      prefix: 'dashboard_'
    }
  }
})

This creates separate cookies for each project:

  • Project 1: habityzer_access_token, habityzer_refresh_token, etc.
  • Project 2: dashboard_access_token, dashboard_refresh_token, etc.

Extending the Module

You can wrap useKindeAuth with your own composable to add custom functionality:

// composables/useAuth.ts
export const useAuth = () => {
  const kindeAuth = useKindeAuth()
  
  // Fetch additional user data from your API
  const fetchUserProfile = async () => {
    const response = await $fetch('/api/user/profile')
    return response
  }
  
  return {
    ...kindeAuth,
    fetchUserProfile
  }
}

Token Management & Auto-Refresh

The module automatically manages token refresh using the stored refresh token. When you call getToken(), it checks if the access token is expired or about to expire (within 5 minutes) and automatically refreshes it:

const { getToken, refreshToken } = useKindeAuth()

// Automatic refresh - the module handles expired tokens
const token = await getToken() // Refreshes automatically if needed

// Use token in API calls
const data = await $fetch('/api/protected-resource', {
  headers: {
    Authorization: `Bearer ${token}`
  }
})

// Manual refresh (optional)
const success = await refreshToken()
if (success) {
  console.log('Token refreshed successfully')
}

How it works:

  1. Access tokens are checked for expiry before being returned
  2. If expired or expiring soon (< 5 minutes), the refresh token is used automatically
  3. New tokens are saved to cookies seamlessly
  4. If refresh fails (expired refresh token), the user is logged out

Proxy Pattern for Backend API

Create a proxy to forward requests with Kinde tokens:

// server/api/backend/[...path].ts
export default defineEventHandler(async (event) => {
  const kindeClient = event.context.kinde
  const token = await kindeClient.getToken()
  
  const path = event.context.params?.path || ''
  const config = useRuntimeConfig()
  
  return $fetch(`${config.backendUrl}/${path}`, {
    headers: {
      Authorization: `Bearer ${token}`
    }
  })
})

Debug Mode

Enable debug endpoints and debug page in development:

// nuxt.config.ts
export default defineNuxtConfig({
  kindeAuth: {
    debug: {
      enabled: process.env.NODE_ENV !== 'production'
    }
  }
})

🛠️ Nuxt DevTools Integration

The module automatically adds a "Kinde Auth" tab to your Nuxt DevTools in development mode!

To use it:

  1. Open your Nuxt app in development mode
  2. Click the Nuxt icon (usually bottom-right corner, or press Shift + Alt/Option + D)
  3. Click the "Kinde Auth" tab in the DevTools sidebar

The DevTools tab displays:

  • ✅ Authentication Status - Live status indicator
  • ✅ Current User Info - See authenticated user details
  • ✅ Token Status - Real-time token expiry countdown with auto-refresh
  • ✅ Configuration - All your Kinde settings (cookie prefix, redirects, etc.)
  • ✅ Interactive Testing - Test token refresh, API calls, and more

No configuration needed! The tab appears automatically when debug.enabled is true.

🔍 Debug Page

When debug mode is enabled, the debug URL will be logged to your browser console on app startup.

Access the interactive debug page at:

http://localhost:3000/__kinde-debug

Quick Access: Bookmark this URL or check your browser console for the debug link!

The debug page provides:

  • ✅ Configuration Display - View all your Kinde settings (cookie prefix, redirects, etc.)
  • ✅ Real-time Token Status - Expiry countdown and status indicators
  • ✅ Interactive Token Refresh - Test manual and automatic refresh
  • ✅ API Call Testing - Verify tokens work with your endpoints
  • ✅ Token Inspection - Decode and view token contents
  • ✅ Auto-refresh - Updates every 10 seconds
  • ✅ User Information - Display current user details

Perfect for testing:

  • Token expiry and refresh functionality
  • API calls with authentication
  • Token lifecycle management
  • Troubleshooting auth issues
  • Verifying configuration settings

Debug API Endpoints

Token Information
GET /api/kinde/debug/token-info

Returns comprehensive token status:

{
  "authenticated": true,
  "tokens": {
    "access": {
      "isExpired": false,
      "isExpiringSoon": true,
      "expiresAt": "2024-01-01T12:00:00Z",
      "timeUntilExpiry": 245,
      "timeUntilExpiryFormatted": "4m 5s"
    },
    "refresh": {
      "exists": true
    }
  },
  "refreshStatus": {
    "canRefresh": true,
    "shouldRefresh": true,
    "reason": "Token expires in less than 5 minutes"
  }
}
Raw Tokens
GET /api/kinde/debug/token

Returns decoded tokens:

{
  "accessToken": {
    "raw": "eyJ...",
    "decoded": { ... }
  },
  "idToken": {
    "raw": "eyJ...",
    "decoded": { ... }
  }
}

TypeScript Support

The module includes full TypeScript support:

import type { KindeUser } from '@habityzer/nuxt-kinde-auth'

const user: KindeUser = {
  id: '123',
  email: 'user@example.com',
  given_name: 'John',
  family_name: 'Doe'
}

Migration Guide

From Custom Kinde Integration

If you have an existing Kinde integration:

  1. Install the module

    pnpm add @habityzer/nuxt-kinde-auth
    
  2. Update nuxt.config.ts

    • Add module to modules array
    • Move Kinde config to kindeAuth key
    • Remove manual runtime config
  3. Remove old files

    • Delete server/middleware/kinde.ts
    • Delete server/api/login.get.ts
    • Delete server/api/logout.get.ts
    • Delete server/api/callback.get.ts
  4. Update imports

    • Replace custom useAuth with useKindeAuth
    • Update API endpoint URLs (/api/login → /api/kinde/login)
  5. Test thoroughly

    • Verify login/logout works
    • Check route protection
    • Test server-side auth

Troubleshooting

"Kinde authentication not initialized"

Cause: Module not loaded or configuration missing.

Solution:

  • Ensure @habityzer/nuxt-kinde-auth is in your modules array
  • Verify authDomain, clientId, and clientSecret are set
  • Restart dev server

Session Not Persisting

Cause: Cookie configuration issues.

Solution:

  • Check cookie.secure setting (should be false for localhost)
  • Verify cookie.sameSite is set to 'lax'
  • Clear browser cookies and try again

Redirect Loop on Login

Cause: Middleware configuration conflict.

Solution:

  • Ensure /api/kinde/callback is in publicRoutes
  • Check that middleware is not blocking auth endpoints
  • Disable module middleware if using custom middleware

Token Refresh Issues

Cause: Refresh token expired or invalid.

Solution:

  • Refresh tokens have a longer lifespan but do expire eventually
  • If refresh fails, users must re-authenticate
  • Check Kinde dashboard for token expiry settings
  • The module automatically handles this by clearing session and requiring login

Requirements

  • Nuxt 4.x
  • Node.js >= 18
  • Kinde account and application

License

MIT

Releases

Versioning and npm publishes run in GitHub Actions when changes are pushed to main or master (see .github/workflows/publish.yml). For commit conventions and npm token setup, see SETUP_NPM_PUBLISH.md.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions:


Made with ❤️ for the Nuxt community

Related Packages

@habityzer/nuxt-symfony-kinde-layer

Shared Nuxt layer for Symfony + Kinde authentication integration

3,634 0
@habityzer/nuxt-openapi-composables

Generate type-safe Nuxt 3 & 4 composables from OpenAPI schemas

1,549 0

Version History

Version Released Vue License
1.4.5 ^3.0 MIT
1.4.4 ^3.0 MIT
1.4.3 ^3.0 MIT
1.4.2 ^3.0 MIT
1.4.1 ^3.0 MIT