nuxt-supabase-team-auth
v0.7.6
Published
Drop-in Nuxt module for team-based authentication with Supabase
Readme
Nuxt Supabase Team Auth
📚 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:
- MODULE_DEVELOPMENT.md - Guide for module contributors
- CONTRIBUTING.md - Contribution guidelines and commit conventions
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/ui1. Install the Module
# Add our module to your existing Nuxt UI app
pnpm add nuxt-supabase-team-authRequired: 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/supabaseusing theinstallModule()pattern - Route protection is configured in
teamAuth.defaultProtection- no need to configure@nuxtjs/supabaseseparately - Environment variables are automatically detected from your
.envfile
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 minLength3. 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 operationsEnvironment 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
- Unauthenticated user tries to access protected route → Gets redirected to login with
?redirect=/original-path - User signs in successfully → Automatically redirected to original path (or configured default)
- No manual redirect code needed → The
AuthSignIncomponent handles everything
Redirect Priority
The module uses this priority when determining where to redirect after login:
- Query parameter -
?redirect=/dashboard(highest priority) - Module config -
teamAuth.redirectToinnuxt.config.ts(fallback) - 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/settings6. 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 headerafter-main-actions- After Settings/Company/Team itemsbefore-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:
- User clicks "Forgot Password" on your sign-in page
- Module navigates to
/auth/forgot-password - User enters email using
<ForgotPasswordForm /> - User receives email with reset link
- Reset link goes to
/auth/confirm?type=recovery&... - 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
- Admin invites user via
<TeamMembersDialog /> - User receives email with invitation link
- Link goes to
/accept-invite?token=... - User sets password using
<PasswordSetupForm /> - 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
topandheightproperties - Dashboard layouts: Handles Nuxt UI Dashboard components specifically
- Custom layouts: Generic
.fixedclass 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 informationteam_members- Team membership and rolesprofiles- User profile dataimpersonation_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 signupaccept-invite- Team invitation acceptanceinvite-member- Send team invitationsrevoke-invitation- Cancel invitationsget-pending-invitations- List pending invitestransfer-ownership- Transfer team ownershipstop-impersonation- Super admin impersonation
Setup Commands
For Local Development:
# Start Supabase locally
supabase start
# Apply database migrations
supabase db resetFor 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 initInstallation & 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 --forceWhat 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 initfirst 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-runWhat 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-123456789abcSafety 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 --usersUsage 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 --statusUpdating 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 deployDevelopment 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 dataTeam 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/...:
- Check environment variables - Ensure
.envhas correct Supabase values - Restart dev server - Environment changes require restart
- Verify Supabase is running - Local development needs
supabase start
Middleware Errors
If you see Unknown route middleware: 'require-auth':
- Restart dev server - Middleware changes require full restart
- 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
- 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
},- 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 }
}
}- Environment variables remain the same:
# The module reads these standard env vars:
SUPABASE_URL=...
SUPABASE_ANON_KEY=...
SUPABASE_SERVICE_KEY=...- 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 buildContributing
Contributions are welcome! Please read our [contributing guidelines] (CONTRIBUTING.md) before submitting a PR.
License
MIT License © 2024
