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

nuxt-supabase-team-auth

v0.7.6

Published

Drop-in Nuxt module for team-based authentication with Supabase

Readme

Nuxt Supabase Team Auth

npm version npm downloads License Nuxt

📚 Documentation Notice
This README is for developers using this module in their applications. If you're looking to contribute to the module itself, please see:

Drop-in Nuxt 3 module for team-based authentication with Supabase.

  • Team-based authentication - Built-in support for multi-user teams
  • 🔐 Role-based access control - Owner, admin, member roles with fine-grained permissions
  • 👥 User impersonation - Super admin impersonation with audit logging
  • 📧 Native invitations - Supabase-native invitation system
  • 🚀 Zero configuration - Works out of the box with sensible defaults
  • 🎨 Nuxt UI components - Beautiful pre-built auth components
  • 📱 SSR ready - Full server-side rendering support
  • 🔒 Security first - Built-in RLS policies and secure session management

Requirements

This module requires the following peer dependencies:

  • Nuxt 4 (^4.0.0)
  • @nuxt/ui (^4.0.0) - UI component framework
  • @nuxt/icon (^1.0.0) - Icon framework (required by Nuxt UI)

The module automatically installs and configures @nuxtjs/supabase (^2.0.0) if not already present.

Quick Setup

Prerequisites

This module requires a working Nuxt UI application. If you don't have one, create it first:

# Create a new Nuxt UI app (recommended)
pnpm create nuxt@latest my-app -t ui

# Or add Nuxt UI to existing Nuxt app
pnpm add @nuxt/ui

1. Install the Module

# Add our module to your existing Nuxt UI app
pnpm add nuxt-supabase-team-auth

Required: Cookie Package Override

Due to a known dependency conflict between Nuxt and Supabase, you need to add a package override to your package.json:

{
  "pnpm": {
    "overrides": {
      "cookie": "0.7.2"
    }
  }
}

Why is this needed? Nuxt/Nitro uses [email protected] while @supabase/ssr expects [email protected]. This override ensures compatibility until Supabase resolves the upstream dependency conflict.

2. Configure Nuxt

Add our module to your nuxt.config.ts. The module automatically registers and configures @nuxtjs/supabase:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/ui',                    // Should already be here
    'nuxt-supabase-team-auth'      // Add our module (auto-configures @nuxtjs/supabase)
  ],

  // Configure team-auth module
  teamAuth: {
    redirectTo: '/dashboard',           // Where to go after login
    loginPage: '/signin',              // Your sign-in page route  
    defaultProtection: 'public',       // Most routes are public by default
    protectedRoutes: ['/dashboard'],   // Only these routes require auth
    publicRoutes: ['/about'],          // Additional public routes
    socialProviders: {
      google: { enabled: true }        // Configure social providers
    },
    debug: true                        // Enable debug logging (optional)
  }
})

Configuration Options

Team Auth Module Configuration (teamAuth key):

| Option | Type | Default | Description | |--------|------|---------|-------------| | redirectTo | string | '/dashboard' | Where to redirect after successful auth | | loginPage | string | '/signin' | Your sign-in page route | | defaultProtection | 'public'\|'protected' | 'public' | Default route protection mode | | protectedRoutes | string[] | ['/dashboard'] | Routes that require authentication (used with defaultProtection: 'public') | | publicRoutes | string[] | [] | Routes that don't require authentication (used with defaultProtection: 'protected') | | socialProviders.google.enabled | boolean | true | Enable Google OAuth | | passwordPolicy | object | See below | Customize password requirements | | debug | boolean | Auto-detected | Enable debug logging |

Route Protection Modes

Public by Default (Recommended)

teamAuth: {
  defaultProtection: 'public',       // Most routes are public
  protectedRoutes: ['/dashboard'],   // Only specific routes need auth
}

Protected by Default

teamAuth: {
  defaultProtection: 'protected',    // All routes require auth
  publicRoutes: ['/', '/about'],     // Except these specific routes
}

Important Notes:

  • The module automatically installs and configures @nuxtjs/supabase using the installModule() pattern
  • Route protection is configured in teamAuth.defaultProtection - no need to configure @nuxtjs/supabase separately
  • Environment variables are automatically detected from your .env file

Password Policy Configuration

Customize password requirements to match your security needs:

// nuxt.config.ts
export default defineNuxtConfig({
  teamAuth: {
    passwordPolicy: {
      minLength: 8,              // Minimum password length (default: 8)
      requireUppercase: false,   // Require uppercase letters (default: false)
      requireLowercase: false,   // Require lowercase letters (default: false)
      requireNumbers: false,     // Require numbers (default: false)
      requireSpecialChars: false,// Require special characters (default: false)
      helpText: 'Custom help'    // Override default help text
    }
  }
})

Password Policy Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | minLength | number | 8 | Minimum password length | | requireUppercase | boolean | false | Require at least one uppercase letter | | requireLowercase | boolean | false | Require at least one lowercase letter | | requireNumbers | boolean | false | Require at least one number | | requireSpecialChars | boolean | false | Require at least one special character | | specialChars | string | !@#$%^&*()_+-=[]{}|;:,.<>? | Define allowed special characters | | helpText | string | Auto-generated | Custom help text to display | | customValidator | function | undefined | Custom validation function |

Example Configurations:

// Default (NIST-aligned) - length over complexity
// No configuration needed, uses defaults

// Traditional complexity requirements
passwordPolicy: {
  minLength: 8,
  requireUppercase: true,
  requireLowercase: true,
  requireNumbers: true,
  helpText: "Must be at least 8 characters with uppercase, lowercase, and numbers"
}

// High security for enterprise
passwordPolicy: {
  minLength: 12,
  requireUppercase: true,
  requireLowercase: true,
  requireNumbers: true,
  requireSpecialChars: true,
  customValidator: (pwd) => {
    const commonPasswords = ['password123', 'admin123']
    return !commonPasswords.includes(pwd.toLowerCase()) || "This password is too common"
  }
}

// Extra long passphrases
passwordPolicy: {
  minLength: 20,
  helpText: "Use a long passphrase (20+ characters)"
}

// Development/testing only
passwordPolicy: {
  minLength: 4,
  helpText: "Min 4 characters (dev only)"
}

Important: Remember to also configure your Supabase project's password policy in supabase/config.toml to match:

[auth]
minimum_password_length = 8  # Should match your minLength

3. Add Required App Structure

Ensure your app.vue has the <UApp> wrapper (should already exist in Nuxt UI apps):

<!-- app.vue -->
<template>
  <UApp>
    <NuxtPage />
  </UApp>
</template>

4. Environment Variables

Set up your Supabase environment variables:

# .env
SUPABASE_URL=http://127.0.0.1:54321          # Your Supabase URL
SUPABASE_ANON_KEY=your-anon-key             # Your Supabase anon key  
SUPABASE_SERVICE_KEY=your-service-key        # For server operations

Environment Variables:

  • SUPABASE_URL - Supabase project URL (used by both client and server)
  • SUPABASE_ANON_KEY - Supabase anon key (used by both client and server)
  • SUPABASE_SERVICE_KEY - Server-side service role key

The module automatically reads these variables and configures them for both client and server usage.

5. Understanding Authentication Redirects

The module handles authentication redirects automatically using a smart redirect system:

How Login Redirects Work

  1. Unauthenticated user tries to access protected route → Gets redirected to login with ?redirect=/original-path
  2. User signs in successfully → Automatically redirected to original path (or configured default)
  3. No manual redirect code needed → The AuthSignIn component handles everything

Redirect Priority

The module uses this priority when determining where to redirect after login:

  1. Query parameter - ?redirect=/dashboard (highest priority)
  2. Module config - teamAuth.redirectTo in nuxt.config.ts (fallback)
  3. Default - /dashboard (if not configured)

Security Features

  • Same-origin validation - Only allows redirects to same domain
  • URL sanitization - Invalid URLs fall back to default redirect
  • Automatic encoding - Handles special characters in redirect URLs

Example Flow

// User visits protected route
// → http://localhost:3000/settings

// Middleware redirects to login
// → http://localhost:3000/signin?redirect=%2Fsettings

// After successful sign-in, automatically redirected to:
// → http://localhost:3000/settings

6. Create Your Pages

Create your authentication pages with proper error handling:

<!-- pages/signin.vue -->
<template>
  <div class="min-h-screen flex items-center justify-center">
    <AuthSignIn
      @error="handleError"
      @forgot-password="handleForgotPassword"
    />
  </div>
</template>

<script setup>
// Redirect authenticated users away from sign-in page
definePageMeta({
  middleware: 'redirect-authenticated'
})

const router = useRouter()
const toast = useToast()

// Note: AuthSignIn component handles post-login redirects automatically
// It reads the ?redirect= query parameter or uses the configured redirectTo route
// No need to manually redirect on @success event

const handleError = (error) => {
  toast.add({
    title: 'Sign In Failed',
    description: error,
    color: 'red'
  })
}

const handleForgotPassword = () => {
  router.push('/auth/forgot-password')
}
</script>
<!-- pages/dashboard.vue -->
<template>
  <div>
    <SignedIn>
      <h1>Welcome to Dashboard</h1>
      <UserButton :show-name="true" />
      <p>Your role: <RoleBadge :role="currentRole" /></p>
    </SignedIn>
  </div>
</template>

<script setup>
// Require authentication for this page
definePageMeta({
  middleware: 'require-auth'
})

const { currentRole } = useTeamAuth()
</script>

Components Reference

Authentication Components

| Component | Description | Key Props | Purpose | |-----------|-------------|-----------|---------| | <AuthSignIn /> | Email/password sign-in form | title, subtitle, showSocialLogin | User authentication | | <AuthSignUpWithTeam /> | Sign-up form with team creation | title, subtitle, showSocialLogin | New user onboarding | | <ForgotPasswordForm /> | Password reset request form | title, subtitle | Initiate password reset | | <ResetPasswordForm /> | New password entry form | title, subtitle | Complete password reset | | <PasswordSetupForm /> | Initial password setup for invites | title, subtitle | Team invitation completion | | <UserButton /> | User avatar with dropdown menu | size, showName, customItems, customItemsPosition | User menu and settings |

Conditional Rendering Components

| Component | Description | Key Props | Purpose | |-----------|-------------|-----------|---------| | <SignedIn> | Shows content only when authenticated | None (slots only) | Conditional auth content | | <SignedOut> | Shows content only when not authenticated | None (slots only) | Login/public content |

User Interface Components

| Component | Description | Key Props | Purpose | |-----------|-------------|-----------|---------| | <ImpersonationBanner /> | Warning banner during impersonation | None (auto-shows) | Impersonation indicator | | <RoleBadge /> | Visual role indicator | role, size, variant | Display user permissions | | <UserCard /> | User profile display card | User data props | Team member listings |

Team Management Components

| Component | Description | Key Props | Purpose | |-----------|-------------|-----------|---------| | <TeamMembersDialog /> | Team member management modal | modelValue | Team member management | | <TeamForm /> | Team creation/editing form | Team data props | Team settings management | | <ProfileForm /> | User profile editing form | Profile data props | User account management |

Dialog & Modal Components

| Component | Description | Key Props | Purpose | |-----------|-------------|-----------|---------| | <DialogBox /> | Base modal component | modelValue, title, subtitle | Custom dialogs | | <FormDialog /> | Modal with form controls | modelValue, title, hasChanges | Form-based dialogs | | <ConfirmDialog /> | Confirmation modal | modelValue, title, message | User confirmations | | <SettingsModal /> | User/team settings modal | modelValue, tab | Settings management |

Specialized Components

| Component | Description | Key Props | Purpose | |-----------|-------------|-----------|---------| | <TeamAuthConfirmation /> | Email confirmation handler | Confirmation data | Email verification | | <SuperAdminImpersonationContent /> | Impersonation control panel | None | Super admin controls |

Component Usage Examples

<!-- Basic authentication -->
<SignedOut>
  <AuthSignIn @success="handleSignIn" />
</SignedOut>

<SignedIn>
  <UserButton :show-name="true" size="md" />
</SignedIn>

<!-- UserButton with custom menu items -->
<UserButton 
  :show-name="true"
  :custom-items="customMenuItems"
  custom-items-position="before-signout"
/>

<!-- Team management -->
<TeamMembersDialog v-model="showTeamDialog" />

<!-- Role display -->
<RoleBadge :role="user.role" size="sm" />

<!-- Impersonation indicator (auto-shows) -->
<ImpersonationBanner />

<!-- Custom dialogs -->
<FormDialog 
  v-model="showForm"
  title="Edit Settings"
  :has-changes="formChanged"
  @save="handleSave"
>
  <!-- Form content -->
</FormDialog>

UserButton Custom Menu Items

The UserButton component supports custom menu items that integrate seamlessly with the built-in user menu:

// Define your custom menu items
import type { CustomMenuItem } from 'nuxt-supabase-team-auth'

const customMenuItems: CustomMenuItem[] = [
  {
    label: 'Billing & Plans',
    icon: 'i-lucide-credit-card',
    to: '/billing',
    requiredRole: 'admin' // Only show for admins and owners
  },
  {
    label: 'Help & Support',
    icon: 'i-lucide-help-circle',
    href: 'https://docs.example.com',
    target: '_blank'
  },
  {
    label: 'Send Feedback',
    icon: 'i-lucide-message-circle',
    onSelect: () => openFeedbackModal(),
    addSeparator: true // Add separator after this item
  }
]
<template>
  <UserButton 
    :custom-items="customMenuItems"
    custom-items-position="before-signout"
    :show-name="true"
  />
</template>

CustomMenuItem Properties:

| Property | Type | Description | |----------|------|-------------| | label | string | Display text for the menu item | | icon | string | Icon name (e.g., 'i-lucide-settings') | | to | string | Internal route to navigate to | | href | string | External URL to navigate to | | target | string | Link target (e.g., '_blank') | | onSelect | function | Custom click handler (overrides navigation) | | disabled | boolean | Disable the menu item | | requiredRole | string | Required role: 'member', 'admin', 'owner', 'super_admin' | | addSeparator | boolean | Add a separator line after this item |

Positioning Options:

  • after-user-info - After user name/email header
  • after-main-actions - After Settings/Company/Team items
  • before-signout - Before sign out section (default)

Custom items are automatically filtered based on user roles and integrate seamlessly with the existing menu structure.

Built-in Pages

The module automatically provides several authentication pages that you can use without creating them manually:

| Route | Component | Purpose | Usage | |-------|-----------|---------|-------| | /auth/forgot-password | <ForgotPasswordForm /> | Password reset request | Link from sign-in page | | /auth/confirm | Email confirmation handler | Email verification | Automatic redirect from email | | /auth/callback | OAuth callback handler | Social login completion | Automatic OAuth redirect | | /accept-invite | <PasswordSetupForm /> | Team invitation acceptance | Email invitation links |

Password Reset Flow

The module provides a complete password reset flow:

  1. User clicks "Forgot Password" on your sign-in page
  2. Module navigates to /auth/forgot-password
  3. User enters email using <ForgotPasswordForm />
  4. User receives email with reset link
  5. Reset link goes to /auth/confirm?type=recovery&...
  6. User sets new password using <ResetPasswordForm />

Example: Adding Forgot Password to Sign-in

<!-- pages/signin.vue -->
<template>
  <AuthSignIn 
    @forgot-password="handleForgotPassword"
  />
</template>

<script setup>
const router = useRouter()

const handleForgotPassword = () => {
  // Module provides this route automatically
  router.push('/auth/forgot-password')
}
</script>

The AuthSignIn component includes a "Forgot password?" button by default that emits the @forgot-password event when clicked.

Team Invitation Flow

  1. Admin invites user via <TeamMembersDialog />
  2. User receives email with invitation link
  3. Link goes to /accept-invite?token=...
  4. User sets password using <PasswordSetupForm />
  5. User joins team automatically

Composables API

useTeamAuth()

The primary composable for team-based authentication and management:

const {
  // Authentication state
  currentUser,              // Ref<User | null>
  currentProfile,           // Ref<Profile | null>
  currentTeam,              // Ref<Team | null>
  currentRole,              // Ref<Role | null>
  teamMembers,              // Ref<TeamMember[]>
  isLoading,                // Ref<boolean>
  
  // Impersonation state
  isImpersonating,          // Ref<boolean>
  impersonatedUser,         // Ref<User | null>
  originalUser,             // Ref<User | null>
  impersonationExpiresAt,   // Ref<Date | null>
  justStartedImpersonation, // Ref<boolean> - UI flag for modal dismissal
  
  // Authentication methods
  signIn,                   // (email: string, password: string) => Promise<void>
  signOut,                  // () => Promise<void>
  signUpWithTeam,           // (email: string, password: string, teamName: string) => Promise<void>
  
  // Profile management
  getProfile,               // () => Promise<Profile | null>
  updateProfile,            // (updates: Partial<Profile>) => Promise<void>
  
  // Team management
  updateTeam,               // (updates: Partial<Team>) => Promise<void>
  renameTeam,               // (name: string) => Promise<void>
  deleteTeam,               // () => Promise<void>
  
  // Member management
  inviteMember,             // (email: string, role: string) => Promise<void>
  updateMemberRole,         // (userId: string, newRole: string) => Promise<void>
  removeMember,             // (userId: string) => Promise<void>
  promote,                  // (userId: string) => Promise<void> - Promote to admin
  demote,                   // (userId: string) => Promise<void> - Demote to member
  transferOwnership,        // (userId: string) => Promise<void>
  getTeamMembers,           // () => Promise<TeamMember[]>
  
  // Invitation management
  getPendingInvitations,    // () => Promise<Invitation[]>
  revokeInvite,            // (userId: string) => Promise<void>
  resendInvite,            // (userId: string) => Promise<void>
  
  // Member profiles
  getTeamMemberProfile,     // (userId: string) => Promise<Profile | null>
  updateTeamMemberProfile,  // (userId: string, updates: Partial<Profile>) => Promise<void>
  
  // Impersonation (Super Admin only)
  startImpersonation,       // (targetUserId: string, reason: string) => Promise<void>
  stopImpersonation,        // () => Promise<void>
  
  // Session management
  sessionHealth,            // () => SessionHealthCheck
  triggerSessionRecovery,   // () => void
  refreshAuthState,         // () => Promise<void>
  
  // Utility methods
  getAvatarFallback,        // (overrides?: {fullName?: string, email?: string}) => string
  clearSuccessFlag          // () => void - Clear justStartedImpersonation flag
} = useTeamAuth()

Role Hierarchy

type Role = 'super_admin' | 'owner' | 'admin' | 'member'
  • Super Admin - Platform-wide access and impersonation
  • Owner - Full team control, can manage all members
  • Admin - Can invite members and manage team settings
  • Member - Basic team access

Error Handling

All methods throw structured errors:

interface AuthError {
  code: string
  message: string
}

Common error codes: SIGNIN_FAILED, SIGNUP_FAILED, PERMISSION_DENIED, TEAM_NOT_FOUND, USER_NOT_FOUND

Middleware

The module provides middleware for route protection:

Available Middleware

| Middleware | Purpose | Usage | |------------|---------|-------| | require-auth | Requires any authenticated user | Basic protected routes | | require-team | Requires team membership | Team-specific pages | | require-role | Requires specific roles | Admin/owner only pages | | redirect-authenticated | Redirects authenticated users | Login pages | | impersonation | Handles impersonation sessions | Super admin features |

Middleware Examples

<!-- Require authentication -->
<script setup>
definePageMeta({
  middleware: 'require-auth'
})
</script>

<!-- Require specific role -->
<script setup>
definePageMeta({
  middleware: 'require-role',
  requireRole: ['admin', 'owner']
})
</script>

<!-- Redirect if already signed in -->
<script setup>
definePageMeta({
  middleware: 'redirect-authenticated'
})
</script>

Server API

The module automatically provides server API endpoints:

Authentication Endpoints

| Endpoint | Method | Purpose | Authorization | |----------|--------|---------|---------------| | /api/signup-with-team | POST | Create user + team | None | | /api/invite-member | POST | Invite team member | Owner/Admin | | /api/accept-invite | POST | Accept invitation | None |

Impersonation Endpoints

| Endpoint | Method | Purpose | Authorization | |----------|--------|---------|---------------| | /api/impersonate | POST | Start impersonation | Super Admin | | /api/stop-impersonation | POST | End impersonation | Super Admin |

Example API Usage

// Sign up with team
const response = await $fetch('/api/signup-with-team', {
  method: 'POST',
  body: {
    email: '[email protected]',
    password: 'securepassword',
    teamName: 'My Team'
  }
})

// Invite member (requires auth headers)
await $fetch('/api/invite-member', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`
  },
  body: {
    email: '[email protected]',
    role: 'member',
    teamId: 'team-id'
  }
})

User Impersonation

Super admins can temporarily act as other users for support and debugging:

Impersonation Features

  • Audit Logging - All sessions logged with reason and timestamps
  • Time Limits - Automatic session expiry (30 minutes default)
  • Visual Indicators - Clear banner when impersonating
  • Session Isolation - Original session preserved and restored

Impersonation Usage

<script setup>
const {
  startImpersonation,
  stopImpersonation,
  isImpersonating,
  currentRole
} = useTeamAuth()

// Check permission
const canImpersonate = computed(() =>
  currentRole.value === 'super_admin'
)

// Start impersonation
const impersonateUser = async (userId) => {
  await startImpersonation(userId, 'Customer support request')
}

// Stop impersonation
const endImpersonation = async () => {
  await stopImpersonation()
}
</script>

<template>
  <!-- Impersonation banner shows automatically -->
  <ImpersonationBanner />

  <!-- Impersonation controls for super admins -->
  <SuperAdminImpersonationContent v-if="canImpersonate" />
</template>

ImpersonationBanner Component

The ImpersonationBanner component provides a visual indicator when a super admin is impersonating another user. It automatically handles content displacement to ensure the banner remains visible without overlapping page content.

Features

  • Automatic positioning: Fixed at the top of the viewport with proper z-index
  • Content displacement: Automatically pushes page content down to remain visible
  • Responsive layout: Adjusts fixed elements (sidebars, navigation) to respect banner height
  • Clean lifecycle: Properly manages styles when banner appears/disappears
  • Real-time countdown: Shows remaining impersonation time
  • Quick exit: One-click "Stop Impersonation" button

Usage

<template>
  <!-- Add to your app layout - usually in app.vue or layout file -->
  <ImpersonationBanner />

  <!-- Your app content -->
  <div>
    <!-- Content automatically pushed down when banner is active -->
  </div>
</template>

Styling Integration

The banner automatically applies the following CSS when active:

/* Applied to <body> when impersonating */
body.has-impersonation-banner {
  padding-top: var(--impersonation-banner-height, 64px);
}

/* Fixed elements are adjusted to respect banner height */
body.has-impersonation-banner .fixed,
body.has-impersonation-banner aside.fixed,
body.has-impersonation-banner main.fixed {
  top: var(--impersonation-banner-height, 64px) !important;
  height: calc(100vh - var(--impersonation-banner-height, 64px)) !important;
}

Dashboard Integration

For applications using fixed layouts (like Nuxt UI Dashboard), the banner automatically:

  • Calculates its actual height dynamically (not hardcoded)
  • Sets CSS custom property --impersonation-banner-height
  • Adjusts dashboard sidebars and main content areas
  • Ensures proper viewport calculations

Properties

The component doesn't accept props - it automatically detects impersonation state from useTeamAuth():

| State | Source | Description | |-------|--------|-------------| | isImpersonating | useTeamAuth() | Controls banner visibility | | impersonatedUser | useTeamAuth() | User being impersonated | | impersonationExpiresAt | useTeamAuth() | Session expiry time |

Layout Compatibility

The banner works with various layout patterns:

  • Static layouts: Uses body padding to push content down
  • Fixed sidebars: Adjusts top and height properties
  • Dashboard layouts: Handles Nuxt UI Dashboard components specifically
  • Custom layouts: Generic .fixed class handling for custom components

No manual styling required - the component handles all layout adjustments automatically.

Database & Edge Functions Setup

The module requires both database schema and Edge Functions to be deployed to your Supabase project:

Required Database Tables

  • teams - Team information
  • team_members - Team membership and roles
  • profiles - User profile data
  • impersonation_sessions - Audit log for impersonation

Required Edge Functions

The module depends on 7 Edge Functions for team management and authentication:

  • create-team-and-owner - Team creation and OAuth signup
  • accept-invite - Team invitation acceptance
  • invite-member - Send team invitations
  • revoke-invitation - Cancel invitations
  • get-pending-invitations - List pending invites
  • transfer-ownership - Transfer team ownership
  • stop-impersonation - Super admin impersonation

Setup Commands

For Local Development:

# Start Supabase locally
supabase start

# Apply database migrations
supabase db reset

For Production/Cloud Deployment:

# Link to your Supabase project
supabase link --project-ref your-project-ref

# Apply database migrations
supabase db push

# Deploy ALL Edge Functions (required!)
supabase functions deploy

# Or deploy individual functions:
supabase functions deploy create-team-and-owner
supabase functions deploy accept-invite
supabase functions deploy invite-member
# ... (deploy all 7 functions)

⚠️ Important: Both database migrations AND Edge Functions must be deployed for the module to work correctly. Missing Edge Functions will result in 404 errors when using team management features.

CLI Commands

The module includes a CLI to simplify database setup and development tasks. After installing the module, the CLI is available in multiple ways:

For projects with the module installed:

# Via node_modules (direct)
./node_modules/.bin/team-auth init

# Via package manager exec
pnpm exec team-auth init
npm exec team-auth init

# Via package.json scripts (recommended)
{
  "scripts": {
    "setup": "team-auth init"
  }
}

For one-time usage or without local install:

# Via npx (downloads and runs)
npx nuxt-supabase-team-auth init

Installation & Setup Commands

npx nuxt-supabase-team-auth init

Initialize the module in your Supabase project by copying migrations and Edge Functions:

# Initialize in a new Supabase project
npx nuxt-supabase-team-auth init

# Force overwrite existing files
npx nuxt-supabase-team-auth init --force

What it does:

  • Copies all required database migrations to supabase/migrations/
  • Copies all Edge Functions to supabase/functions/
  • Detects conflicting tables and warns before proceeding
  • Sets up version tracking for future updates
  • Adds npm script shortcuts to your package.json

Prerequisites:

  • Must be run from a Supabase project directory (with supabase/config.toml)
  • Run supabase init first if starting fresh

npx nuxt-supabase-team-auth migrate

Apply new migrations when updating the module:

# Check and apply new migrations
npx nuxt-supabase-team-auth migrate

# Preview what would be applied without making changes
npx nuxt-supabase-team-auth migrate --dry-run

What it does:

  • Compares your current module version with installed version
  • Copies only new migration files and Edge Functions
  • Updates version tracking to prevent duplicate applications
  • Automatically applies migrations to local database if Supabase is running

Development & Debugging Commands

npx nuxt-supabase-team-auth cleanup

Clean up test data and manage teams during development:

# Reset entire database (like supabase db reset)
npx nuxt-supabase-team-auth cleanup --all

# Clean only test users (emails ending with @example.com)
npx nuxt-supabase-team-auth cleanup --test-data

# Delete a specific team by ID
npx nuxt-supabase-team-auth cleanup --team 12345678-1234-1234-1234-123456789abc

Safety features:

  • Confirmation prompts for destructive operations
  • UUID validation for team IDs
  • Uses specialized Edge Functions to bypass RLS constraints

npx nuxt-supabase-team-auth db

Inspect your database and Supabase services:

# Show Supabase services status
npx nuxt-supabase-team-auth db --status

# List all teams in the database
npx nuxt-supabase-team-auth db --teams

# List all users (limited to 50 for performance)
npx nuxt-supabase-team-auth db --users

Usage Examples

Setting up a new project:

# 1. Create Supabase project
supabase init

# 2. Initialize team-auth
npx nuxt-supabase-team-auth init

# 3. Start local development
supabase start

# 4. Verify setup
npx nuxt-supabase-team-auth db --status

Updating to a new module version:

# 1. Update the npm package
pnpm update nuxt-supabase-team-auth

# 2. Apply new migrations
npx nuxt-supabase-team-auth migrate

# 3. Deploy to production when ready
supabase db push
supabase functions deploy

Development workflow:

# Clean test data between tests
npx nuxt-supabase-team-auth cleanup --test-data

# Check what's in the database
npx nuxt-supabase-team-auth db --teams
npx nuxt-supabase-team-auth db --users

# Delete problematic test team
npx nuxt-supabase-team-auth cleanup --team <team-id>

Integration with package.json

After running the init command, you can add convenience scripts to your package.json:

{
  "scripts": {
    "setup": "team-auth init",
    "migrate": "team-auth migrate",
    "db:clean": "team-auth cleanup --test-data"
  }
}

This allows team members to easily run commands:

pnpm run setup     # Initialize team-auth
pnpm run migrate   # Apply new migrations  
pnpm run db:clean  # Clean test data

Team Management Examples

Basic Team Operations

<script setup>
const {
  currentTeam,
  currentRole,
  teamMembers,
  inviteMember,
  updateMemberRole,
  removeMember
} = useTeamAuth()

// Permission checks
const canInviteMembers = computed(() => 
  ['owner', 'admin'].includes(currentRole.value)
)

// Invite new member
const handleInvite = async (email, role) => {
  try {
    await inviteMember(email, role)
    console.log('Invitation sent successfully')
  } catch (error) {
    console.error('Failed to invite:', error.message)
  }
}

// Promote member
const promoteMember = async (userId) => {
  await updateMemberRole(userId, 'admin')
}

// Remove member
const handleRemove = async (userId) => {
  const confirmed = confirm('Remove this member?')
  if (confirmed) {
    await removeMember(userId)
  }
}
</script>

<template>
  <div>
    <h2>{{ currentTeam?.name }}</h2>
    <p>Your role: <RoleBadge :role="currentRole" /></p>
    
    <!-- Team member list -->
    <div v-for="member in teamMembers" :key="member.user_id">
      <UserCard :user="member.user" :role="member.role" />
      <UButton 
        v-if="canInviteMembers && member.role === 'member'"
        @click="promoteMember(member.user_id)"
      >
        Promote to Admin
      </UButton>
    </div>
    
    <!-- Invite new member -->
    <TeamMembersDialog v-if="canInviteMembers" />
  </div>
</template>

Troubleshooting

Common Integration Issues

Module Order Doesn't Matter

Module order in your nuxt.config.ts does not affect functionality:

// Both work the same way
modules: ['@nuxt/ui', 'nuxt-supabase-team-auth']
modules: ['nuxt-supabase-team-auth', '@nuxt/ui']

Missing Nuxt UI Setup

If you see errors about missing components:

# Ensure you started with a Nuxt UI app
pnpm create nuxt@latest my-app -t ui

# Verify app.vue has UApp wrapper:
<template>
  <UApp>
    <NuxtPage />
  </UApp>
</template>

Server API Errors

If you see errors like undefined/functions/v1/...:

  1. Check environment variables - Ensure .env has correct Supabase values
  2. Restart dev server - Environment changes require restart
  3. Verify Supabase is running - Local development needs supabase start

Middleware Errors

If you see Unknown route middleware: 'require-auth':

  1. Restart dev server - Middleware changes require full restart
  2. Verify module installation - Run pnpm install

SSR Hydration Issues

Hydration mismatches are handled automatically by our components using ClientOnly wrappers where needed.

Version Compatibility

This module is tested with:

  • Nuxt: ^3.17.5
  • @nuxt/ui: ^3.1.3
  • @nuxt/icon: ^1.2.49

Common Patterns

Starting Fresh

# Create new Nuxt UI app
pnpm create nuxt@latest my-team-app -t ui
cd my-team-app

# Add our module
pnpm add nuxt-supabase-team-auth

# Configure in nuxt.config.ts (see configuration section above)
# Add environment variables
# Start building!

Integration Testing

Use our minimal test app as reference:

  • test-projects/minimal-nuxt-ui-app/ - Working integration example
  • Shows proper component usage and middleware setup

Migration from Previous Versions

If you're updating from a previous version of this module (< v0.3.6), you need to update your configuration:

Required Changes

  1. Remove separate @nuxtjs/supabase configuration:
// REMOVE: No longer needed - module handles this automatically
supabase: {
  url: process.env.SUPABASE_URL,           // Remove
  key: process.env.SUPABASE_ANON_KEY,      // Remove
  redirectOptions: { ... }                 // Remove
},
  1. Update teamAuth configuration:
// BEFORE: Mixed configuration
teamAuth: {
  supabaseUrl: process.env.SUPABASE_URL,    // Remove
  supabaseKey: process.env.SUPABASE_ANON_KEY, // Remove
  redirectTo: '/dashboard',
  loginPage: '/signin',
}

// AFTER: Unified configuration with route protection
teamAuth: {
  redirectTo: '/dashboard',
  loginPage: '/signin', 
  defaultProtection: 'public',              // NEW: Route protection mode
  protectedRoutes: ['/dashboard'],          // NEW: Specific protected routes
  socialProviders: {
    google: { enabled: true }
  }
}
  1. Environment variables remain the same:
# The module reads these standard env vars:
SUPABASE_URL=...
SUPABASE_ANON_KEY=...
SUPABASE_SERVICE_KEY=...
  1. Add error handling to auth pages:
<!-- BEFORE: Only success handler -->
<AuthSignIn @success="handleSignIn" />

<!-- AFTER: Add error handler for user feedback -->
<AuthSignIn 
  @success="handleSignIn" 
  @error="handleError" 
/>

<script setup>
const toast = useToast()
const handleError = (error) => {
  toast.add({ title: 'Sign In Failed', description: error, color: 'red' })
}
</script>

#### Benefits of New Architecture

- **Simplified Configuration** - Single `teamAuth` config block, no separate Supabase setup
- **Automatic Dependency Management** - Uses `installModule()` pattern for proper integration
- **Flexible Route Protection** - Choose between public-by-default or protected-by-default modes
- **Standard Environment Variables** - Uses standard Supabase env var names
- **Improved Error Handling** - Components emit errors for proper user feedback

## Development

```bash
# Install dependencies
pnpm install

# Generate types
pnpm run types:generate

# Start the playground
pnpm run dev

# Run tests
pnpm run test

# Build the module
pnpm run build

Contributing

Contributions are welcome! Please read our [contributing guidelines] (CONTRIBUTING.md) before submitting a PR.

License

MIT License © 2024