Last updated: Sep 1, 2025, 01:10 PM UTC

JWT Authentication Flow Guide with Supabase Auth & Licensing

Purpose: Complete JWT authentication implementation guide for Sasha Studio with Supabase Auth and subscription licensing model
Target Users: Development team, security architects, backend developers


Overview

This guide provides comprehensive implementation details for JWT-based authentication in Sasha Studio, leveraging Supabase Auth for user management and implementing a sophisticated licensing model for subscription-based access control.

Related Guides:

Authentication Architecture

graph TB subgraph "Client Application" A[Web Interface] B[Mobile App] C[API Client] end subgraph "Sasha Studio Backend" D[JWT Middleware] E[License Validator] F[Feature Gate] G[API Routes] end subgraph "Supabase Services" H[GoTrue Auth] I[PostgreSQL] J[Row Level Security] end subgraph "Licensing System" K[Subscription Manager] L[Usage Tracker] M[Billing Integration] end A --> D B --> D C --> D D --> E E --> F F --> G D --> H H --> I I --> J E --> K K --> L L --> M

JWT Token Structure & Flow

Access Token Payload

{
  "aud": "authenticated",
  "exp": 1704123456,
  "iat": 1704122556,
  "iss": "https://your-project.supabase.co/auth/v1",
  "sub": "user-uuid-here",
  "email": "user@example.com",
  "phone": "",
  "app_metadata": {
    "provider": "email",
    "providers": ["email"]
  },
  "user_metadata": {
    "full_name": "John Doe",
    "avatar_url": "https://example.com/avatar.jpg"
  },
  "role": "authenticated",
  "aal": "aal1",
  "amr": [{"method": "password", "timestamp": 1704122556}],
  "session_id": "session-uuid-here",
  
  // Custom Sasha Studio Claims
  "workspace_id": "workspace-uuid",
  "subscription_tier": "pro",
  "license_status": "active",
  "feature_flags": {
    "ai_models": ["claude-3", "gpt-4"],
    "storage_gb": 100,
    "monthly_queries": 10000,
    "team_members": 25
  },
  "permissions": [
    "chat:create",
    "file:upload",
    "guide:access",
    "publish:create",
    "admin:workspace"
  ]
}

Refresh Token Flow

sequenceDiagram participant C as Client participant A as Sasha API participant S as Supabase Auth participant D as Database Note over C,D: Initial Authentication C->>S: POST /auth/v1/token (email/password) S->>D: Verify credentials D-->>S: User profile + subscription S->>S: Generate JWT + Refresh Token S-->>C: JWT (15min) + Refresh Token (7d) Note over C,D: Authenticated API Request C->>A: API Request + JWT A->>A: Verify JWT signature A->>A: Check license status A->>A: Validate feature permissions A-->>C: API Response Note over C,D: Token Refresh C->>S: POST /auth/v1/token (refresh_token) S->>D: Verify refresh token S->>D: Check subscription status D-->>S: Updated user + license data S->>S: Generate new JWT S-->>C: New JWT + Refresh Token

Licensing Model Implementation

Database Schema

-- Enhanced Users Table (extends Supabase auth.users)
CREATE TABLE public.user_profiles (
  id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
  full_name TEXT,
  avatar_url TEXT,
  workspace_id UUID REFERENCES workspaces(id),
  role workspace_role DEFAULT 'member',
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Workspaces (Multi-tenant Organizations)
CREATE TABLE public.workspaces (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  name TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  owner_id UUID REFERENCES auth.users(id) NOT NULL,
  subscription_id UUID REFERENCES subscriptions(id),
  settings JSONB DEFAULT '{}',
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Subscription Management
CREATE TABLE public.subscriptions (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  workspace_id UUID REFERENCES workspaces(id) ON DELETE CASCADE,
  tier subscription_tier NOT NULL DEFAULT 'free',
  status subscription_status NOT NULL DEFAULT 'active',
  current_period_start TIMESTAMPTZ NOT NULL,
  current_period_end TIMESTAMPTZ NOT NULL,
  stripe_subscription_id TEXT,
  stripe_customer_id TEXT,
  billing_email TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Feature Limits by Subscription Tier
CREATE TABLE public.subscription_limits (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  tier subscription_tier NOT NULL,
  feature_name TEXT NOT NULL,
  limit_value INTEGER NOT NULL,
  limit_type limit_type NOT NULL DEFAULT 'monthly',
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(tier, feature_name)
);

-- Usage Tracking
CREATE TABLE public.usage_records (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  workspace_id UUID REFERENCES workspaces(id) ON DELETE CASCADE,
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
  feature_name TEXT NOT NULL,
  usage_count INTEGER DEFAULT 1,
  metadata JSONB DEFAULT '{}',
  recorded_at TIMESTAMPTZ DEFAULT NOW(),
  period_start TIMESTAMPTZ NOT NULL,
  period_end TIMESTAMPTZ NOT NULL
);

-- Custom Types
CREATE TYPE subscription_tier AS ENUM ('free', 'starter', 'pro', 'enterprise');
CREATE TYPE subscription_status AS ENUM ('active', 'trialing', 'past_due', 'canceled', 'unpaid');
CREATE TYPE workspace_role AS ENUM ('owner', 'admin', 'member', 'viewer');
CREATE TYPE limit_type AS ENUM ('daily', 'monthly', 'total');

Row Level Security Policies

-- Users can only access their own profile
CREATE POLICY "Users can access own profile" ON user_profiles
  FOR ALL USING (auth.uid() = id);

-- Workspace access based on membership
CREATE POLICY "Workspace members can access workspace" ON workspaces
  FOR SELECT USING (
    id IN (
      SELECT workspace_id FROM user_profiles 
      WHERE id = auth.uid()
    )
  );

-- Subscription access for workspace owners/admins
CREATE POLICY "Workspace admins can access subscription" ON subscriptions
  FOR ALL USING (
    workspace_id IN (
      SELECT workspace_id FROM user_profiles 
      WHERE id = auth.uid() 
      AND role IN ('owner', 'admin')
    )
  );

-- Usage records accessible by workspace members
CREATE POLICY "Workspace members can view usage" ON usage_records
  FOR SELECT USING (
    workspace_id IN (
      SELECT workspace_id FROM user_profiles 
      WHERE id = auth.uid()
    )
  );

JWT Middleware Implementation

Node.js/Express Middleware

// middleware/auth.js
const jwt = require('jsonwebtoken');
const { createClient } = require('@supabase/supabase-js');

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_ROLE_KEY
);

class AuthMiddleware {
  /**
   * Verify JWT and extract user/license information
   */
  static async verifyToken(req, res, next) {
    try {
      const token = this.extractToken(req);
      if (!token) {
        return res.status(401).json({ error: 'No token provided' });
      }

      // Verify token with Supabase
      const { data: user, error } = await supabase.auth.getUser(token);
      if (error || !user) {
        return res.status(401).json({ error: 'Invalid token' });
      }

      // Enrich with license information
      const enrichedUser = await this.enrichUserWithLicense(user.user);
      
      req.user = enrichedUser;
      req.token = token;
      
      next();
    } catch (error) {
      console.error('Auth middleware error:', error);
      return res.status(401).json({ error: 'Token verification failed' });
    }
  }

  /**
   * Extract token from Authorization header or cookies
   */
  static extractToken(req) {
    // Try Authorization header first
    const authHeader = req.headers.authorization;
    if (authHeader && authHeader.startsWith('Bearer ')) {
      return authHeader.substring(7);
    }

    // Fallback to httpOnly cookie
    return req.cookies?.['sasha-token'];
  }

  /**
   * Enrich user with workspace and license data
   */
  static async enrichUserWithLicense(user) {
    const { data: profile } = await supabase
      .from('user_profiles')
      .select(`
        *,
        workspace:workspaces(
          id,
          name,
          slug,
          subscription:subscriptions(
            id,
            tier,
            status,
            current_period_start,
            current_period_end
          )
        )
      `)
      .eq('id', user.id)
      .single();

    return {
      ...user,
      profile,
      workspace: profile?.workspace,
      subscription: profile?.workspace?.subscription,
      permissions: await this.getUserPermissions(user.id, profile?.workspace?.id)
    };
  }

  /**
   * Get user permissions based on role and subscription
   */
  static async getUserPermissions(userId, workspaceId) {
    if (!workspaceId) return [];

    const { data: profile } = await supabase
      .from('user_profiles')
      .select('role')
      .eq('id', userId)
      .eq('workspace_id', workspaceId)
      .single();

    const { data: subscription } = await supabase
      .from('subscriptions')
      .select('tier, status')
      .eq('workspace_id', workspaceId)
      .single();

    return this.calculatePermissions(profile?.role, subscription);
  }

  /**
   * Calculate permissions based on role and subscription tier
   */
  static calculatePermissions(role, subscription) {
    const basePermissions = {
      viewer: ['chat:read', 'file:read'],
      member: ['chat:create', 'file:upload', 'guide:access'],
      admin: ['chat:create', 'file:upload', 'guide:access', 'publish:create', 'workspace:manage'],
      owner: ['*'] // All permissions
    };

    const tierPermissions = {
      free: ['basic_models'],
      starter: ['basic_models', 'file_storage_1gb'],
      pro: ['advanced_models', 'file_storage_10gb', 'custom_guides'],
      enterprise: ['all_models', 'unlimited_storage', 'custom_guides', 'priority_support']
    };

    const rolePerms = basePermissions[role] || [];
    const tierPerms = subscription?.status === 'active' 
      ? tierPermissions[subscription.tier] || []
      : tierPermissions.free;

    return [...rolePerms, ...tierPerms];
  }
}

module.exports = AuthMiddleware;

License Validation Middleware

// middleware/license.js
class LicenseMiddleware {
  /**
   * Check if user has required feature access
   */
  static requireFeature(featureName) {
    return async (req, res, next) => {
      try {
        const user = req.user;
        if (!user) {
          return res.status(401).json({ error: 'Authentication required' });
        }

        const hasAccess = await this.checkFeatureAccess(
          user.workspace.id,
          featureName,
          user.id
        );

        if (!hasAccess.allowed) {
          return res.status(403).json({ 
            error: 'Feature not available in your subscription',
            feature: featureName,
            reason: hasAccess.reason,
            upgrade_required: hasAccess.upgradeRequired
          });
        }

        // Track usage
        await this.trackUsage(user.workspace.id, user.id, featureName);
        
        next();
      } catch (error) {
        console.error('License check error:', error);
        return res.status(500).json({ error: 'License validation failed' });
      }
    };
  }

  /**
   * Check if workspace has access to specific feature
   */
  static async checkFeatureAccess(workspaceId, featureName, userId) {
    // Get subscription details
    const { data: workspace } = await supabase
      .from('workspaces')
      .select(`
        subscription:subscriptions(
          tier,
          status,
          current_period_end
        )
      `)
      .eq('id', workspaceId)
      .single();

    const subscription = workspace?.subscription;
    
    // Check subscription status
    if (!subscription || subscription.status !== 'active') {
      return {
        allowed: false,
        reason: 'Subscription inactive',
        upgradeRequired: true
      };
    }

    // Check if subscription expired
    if (new Date(subscription.current_period_end) < new Date()) {
      return {
        allowed: false,
        reason: 'Subscription expired',
        upgradeRequired: true
      };
    }

    // Get feature limits for subscription tier
    const { data: limits } = await supabase
      .from('subscription_limits')
      .select('*')
      .eq('tier', subscription.tier)
      .eq('feature_name', featureName);

    if (!limits || limits.length === 0) {
      // Feature not defined for this tier - allow by default for higher tiers
      const tierHierarchy = ['free', 'starter', 'pro', 'enterprise'];
      const currentTierIndex = tierHierarchy.indexOf(subscription.tier);
      const isHighTier = currentTierIndex >= 2; // pro and enterprise
      
      return {
        allowed: isHighTier,
        reason: isHighTier ? 'Allowed for tier' : 'Feature not available in tier',
        upgradeRequired: !isHighTier
      };
    }

    // Check usage limits
    const limit = limits[0];
    const currentUsage = await this.getCurrentUsage(
      workspaceId, 
      featureName, 
      limit.limit_type
    );

    if (currentUsage >= limit.limit_value) {
      return {
        allowed: false,
        reason: `${limit.limit_type} limit exceeded (${currentUsage}/${limit.limit_value})`,
        upgradeRequired: true
      };
    }

    return {
      allowed: true,
      remaining: limit.limit_value - currentUsage
    };
  }

  /**
   * Get current usage for a feature in the specified period
   */
  static async getCurrentUsage(workspaceId, featureName, limitType) {
    const now = new Date();
    let periodStart;

    switch (limitType) {
      case 'daily':
        periodStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        break;
      case 'monthly':
        periodStart = new Date(now.getFullYear(), now.getMonth(), 1);
        break;
      case 'total':
        periodStart = new Date('1970-01-01');
        break;
      default:
        periodStart = new Date(now.getFullYear(), now.getMonth(), 1);
    }

    const { data, error } = await supabase
      .from('usage_records')
      .select('usage_count')
      .eq('workspace_id', workspaceId)
      .eq('feature_name', featureName)
      .gte('recorded_at', periodStart.toISOString());

    if (error) {
      console.error('Usage query error:', error);
      return 0;
    }

    return data?.reduce((total, record) => total + record.usage_count, 0) || 0;
  }

  /**
   * Track feature usage
   */
  static async trackUsage(workspaceId, userId, featureName, count = 1, metadata = {}) {
    const now = new Date();
    const periodStart = new Date(now.getFullYear(), now.getMonth(), 1);
    const periodEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);

    await supabase
      .from('usage_records')
      .insert({
        workspace_id: workspaceId,
        user_id: userId,
        feature_name: featureName,
        usage_count: count,
        metadata,
        recorded_at: now.toISOString(),
        period_start: periodStart.toISOString(),
        period_end: periodEnd.toISOString()
      });
  }
}

module.exports = LicenseMiddleware;

API Integration Examples

Protected Route Implementation

// routes/chat.js
const express = require('express');
const AuthMiddleware = require('../middleware/auth');
const LicenseMiddleware = require('../middleware/license');

const router = express.Router();

// Apply authentication to all chat routes
router.use(AuthMiddleware.verifyToken);

// Create new chat session
router.post('/sessions', 
  LicenseMiddleware.requireFeature('chat_sessions'),
  async (req, res) => {
    try {
      const { title, model_preference } = req.body;
      
      // Validate model access
      const modelAccess = await LicenseMiddleware.checkFeatureAccess(
        req.user.workspace.id,
        `model_${model_preference}`,
        req.user.id
      );

      if (!modelAccess.allowed) {
        return res.status(403).json({
          error: 'Model not available in your subscription',
          available_models: await getAvailableModels(req.user.subscription.tier)
        });
      }

      // Create session
      const session = await createChatSession({
        workspace_id: req.user.workspace.id,
        user_id: req.user.id,
        title,
        model_preference
      });

      res.json({ session });
    } catch (error) {
      console.error('Create session error:', error);
      res.status(500).json({ error: 'Failed to create session' });
    }
  }
);

// Send message in chat
router.post('/sessions/:sessionId/messages',
  LicenseMiddleware.requireFeature('ai_queries'),
  async (req, res) => {
    try {
      const { sessionId } = req.params;
      const { content, model } = req.body;

      // Verify session ownership
      const session = await getChatSession(sessionId, req.user.workspace.id);
      if (!session) {
        return res.status(404).json({ error: 'Session not found' });
      }

      // Process message with AI
      const response = await processAIMessage({
        content,
        model,
        user: req.user,
        session
      });

      res.json({ message: response });
    } catch (error) {
      console.error('Send message error:', error);
      res.status(500).json({ error: 'Failed to process message' });
    }
  }
);

module.exports = router;

Frontend Token Management

// utils/auth.js (Frontend)
class AuthManager {
  constructor() {
    this.supabase = createClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
    );
    
    this.setupTokenRefresh();
  }

  /**
   * Login with email/password
   */
  async login(email, password) {
    try {
      const { data, error } = await this.supabase.auth.signInWithPassword({
        email,
        password
      });

      if (error) throw error;

      // Store tokens securely
      this.storeTokens(data.session);
      
      return { user: data.user, session: data.session };
    } catch (error) {
      console.error('Login error:', error);
      throw new Error(error.message || 'Login failed');
    }
  }

  /**
   * Logout user
   */
  async logout() {
    try {
      const { error } = await this.supabase.auth.signOut();
      if (error) throw error;

      this.clearTokens();
      window.location.href = '/login';
    } catch (error) {
      console.error('Logout error:', error);
    }
  }

  /**
   * Get current session with license data
   */
  async getSession() {
    try {
      const { data: { session }, error } = await this.supabase.auth.getSession();
      
      if (error) throw error;
      if (!session) return null;

      // Enrich with license information
      const enrichedSession = await this.enrichSessionWithLicense(session);
      
      return enrichedSession;
    } catch (error) {
      console.error('Get session error:', error);
      return null;
    }
  }

  /**
   * Setup automatic token refresh
   */
  setupTokenRefresh() {
    this.supabase.auth.onAuthStateChange(async (event, session) => {
      if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {
        this.storeTokens(session);
      } else if (event === 'SIGNED_OUT') {
        this.clearTokens();
      }
    });
  }

  /**
   * Store tokens securely (httpOnly cookies in production)
   */
  storeTokens(session) {
    if (!session) return;

    // In development, use localStorage
    if (process.env.NODE_ENV === 'development') {
      localStorage.setItem('sasha-token', session.access_token);
      localStorage.setItem('sasha-refresh', session.refresh_token);
    } else {
      // In production, tokens should be stored in httpOnly cookies
      // This would be handled by your backend API
      document.cookie = `sasha-token=${session.access_token}; HttpOnly; Secure; SameSite=Strict; Max-Age=900`; // 15 min
      document.cookie = `sasha-refresh=${session.refresh_token}; HttpOnly; Secure; SameSite=Strict; Max-Age=604800`; // 7 days
    }
  }

  /**
   * Clear stored tokens
   */
  clearTokens() {
    if (process.env.NODE_ENV === 'development') {
      localStorage.removeItem('sasha-token');
      localStorage.removeItem('sasha-refresh');
    } else {
      document.cookie = 'sasha-token=; Max-Age=0';
      document.cookie = 'sasha-refresh=; Max-Age=0';
    }
  }

  /**
   * Enrich session with workspace and license data
   */
  async enrichSessionWithLicense(session) {
    try {
      const { data: profile, error } = await this.supabase
        .from('user_profiles')
        .select(`
          *,
          workspace:workspaces(
            id,
            name,
            slug,
            subscription:subscriptions(
              id,
              tier,
              status,
              current_period_start,
              current_period_end
            )
          )
        `)
        .eq('id', session.user.id)
        .single();

      if (error) throw error;

      return {
        ...session,
        user: {
          ...session.user,
          profile,
          workspace: profile?.workspace,
          subscription: profile?.workspace?.subscription
        }
      };
    } catch (error) {
      console.error('Enrich session error:', error);
      return session;
    }
  }

  /**
   * Make authenticated API request
   */
  async apiRequest(url, options = {}) {
    const session = await this.getSession();
    if (!session) {
      throw new Error('Not authenticated');
    }

    const headers = {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${session.access_token}`,
      ...options.headers
    };

    const response = await fetch(url, {
      ...options,
      headers
    });

    if (response.status === 401) {
      // Token expired, try to refresh
      await this.supabase.auth.refreshSession();
      const newSession = await this.getSession();
      
      if (newSession) {
        // Retry with new token
        headers.Authorization = `Bearer ${newSession.access_token}`;
        return fetch(url, { ...options, headers });
      } else {
        // Refresh failed, redirect to login
        this.logout();
        throw new Error('Session expired');
      }
    }

    return response;
  }
}

export default new AuthManager();

Configuration & Environment

Environment Variables

# Supabase Configuration
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

# JWT Configuration
JWT_SECRET_KEY=your-jwt-secret
JWT_ACCESS_TOKEN_EXPIRES_IN=15m
JWT_REFRESH_TOKEN_EXPIRES_IN=7d

# Cookie Configuration
COOKIE_SECRET=your-cookie-secret
COOKIE_DOMAIN=your-domain.com
COOKIE_SECURE=true

# Licensing Configuration
STRIPE_SECRET_KEY=sk_test_your-stripe-key
STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret

# Feature Flags
ENABLE_SOCIAL_LOGIN=true
ENABLE_MFA=false
ENABLE_USAGE_TRACKING=true

Supabase Configuration

-- Insert default subscription limits
INSERT INTO subscription_limits (tier, feature_name, limit_value, limit_type) VALUES
-- Free tier limits
('free', 'ai_queries', 100, 'monthly'),
('free', 'file_storage_mb', 100, 'total'),
('free', 'team_members', 1, 'total'),
('free', 'workspaces', 1, 'total'),

-- Starter tier limits  
('starter', 'ai_queries', 1000, 'monthly'),
('starter', 'file_storage_mb', 1000, 'total'),
('starter', 'team_members', 5, 'total'),
('starter', 'workspaces', 3, 'total'),

-- Pro tier limits
('pro', 'ai_queries', 10000, 'monthly'),
('pro', 'file_storage_mb', 10000, 'total'),
('pro', 'team_members', 25, 'total'),
('pro', 'workspaces', 10, 'total'),

-- Enterprise tier (unlimited)
('enterprise', 'ai_queries', 999999, 'monthly'),
('enterprise', 'file_storage_mb', 999999, 'total'),
('enterprise', 'team_members', 999999, 'total'),
('enterprise', 'workspaces', 999999, 'total');

-- Enable Row Level Security
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE workspaces ENABLE ROW LEVEL SECURITY;
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
ALTER TABLE usage_records ENABLE ROW LEVEL SECURITY;

Testing & Validation

Authentication Test Suite

// tests/auth.test.js
const request = require('supertest');
const app = require('../app');

describe('Authentication Flow', () => {
  let authToken;
  let refreshToken;

  beforeEach(async () => {
    // Setup test user and workspace
    await setupTestData();
  });

  afterEach(async () => {
    // Cleanup test data
    await cleanupTestData();
  });

  describe('Login Flow', () => {
    test('should login with valid credentials', async () => {
      const response = await request(app)
        .post('/auth/login')
        .send({
          email: 'test@example.com',
          password: 'testpassword'
        });

      expect(response.status).toBe(200);
      expect(response.body).toHaveProperty('access_token');
      expect(response.body).toHaveProperty('refresh_token');
      expect(response.body).toHaveProperty('user');
      
      authToken = response.body.access_token;
      refreshToken = response.body.refresh_token;
    });

    test('should reject invalid credentials', async () => {
      const response = await request(app)
        .post('/auth/login')
        .send({
          email: 'test@example.com',
          password: 'wrongpassword'
        });

      expect(response.status).toBe(401);
      expect(response.body).toHaveProperty('error');
    });
  });

  describe('Token Validation', () => {
    test('should access protected route with valid token', async () => {
      const response = await request(app)
        .get('/api/chat/sessions')
        .set('Authorization', `Bearer ${authToken}`);

      expect(response.status).toBe(200);
    });

    test('should reject request with invalid token', async () => {
      const response = await request(app)
        .get('/api/chat/sessions')
        .set('Authorization', 'Bearer invalid-token');

      expect(response.status).toBe(401);
    });

    test('should refresh expired token', async () => {
      const response = await request(app)
        .post('/auth/refresh')
        .send({
          refresh_token: refreshToken
        });

      expect(response.status).toBe(200);
      expect(response.body).toHaveProperty('access_token');
    });
  });

  describe('License Validation', () => {
    test('should allow feature access for valid subscription', async () => {
      const response = await request(app)
        .post('/api/chat/sessions')
        .set('Authorization', `Bearer ${authToken}`)
        .send({
          title: 'Test Session',
          model_preference: 'claude-3'
        });

      expect(response.status).toBe(200);
    });

    test('should deny feature access for insufficient license', async () => {
      // Create user with free tier
      const freeUserToken = await createFreeUserToken();
      
      const response = await request(app)
        .post('/api/chat/sessions')
        .set('Authorization', `Bearer ${freeUserToken}`)
        .send({
          title: 'Test Session',
          model_preference: 'gpt-4' // Not available in free tier
        });

      expect(response.status).toBe(403);
      expect(response.body).toHaveProperty('upgrade_required', true);
    });

    test('should track usage correctly', async () => {
      // Make multiple requests
      for (let i = 0; i < 3; i++) {
        await request(app)
          .post('/api/chat/sessions/:sessionId/messages')
          .set('Authorization', `Bearer ${authToken}`)
          .send({
            content: `Test message ${i}`,
            model: 'claude-3'
          });
      }

      // Check usage tracking
      const usageResponse = await request(app)
        .get('/api/usage/current')
        .set('Authorization', `Bearer ${authToken}`);

      expect(usageResponse.status).toBe(200);
      expect(usageResponse.body.ai_queries).toBe(3);
    });
  });
});

Error Handling & Edge Cases

Authentication Error Scenarios

// utils/authErrors.js
class AuthError extends Error {
  constructor(message, code, statusCode = 401) {
    super(message);
    this.name = 'AuthError';
    this.code = code;
    this.statusCode = statusCode;
  }
}

class LicenseError extends Error {
  constructor(message, feature, upgradeRequired = true) {
    super(message);
    this.name = 'LicenseError';
    this.feature = feature;
    this.upgradeRequired = upgradeRequired;
    this.statusCode = 403;
  }
}

// Error handler middleware
const errorHandler = (error, req, res, next) => {
  if (error instanceof AuthError) {
    return res.status(error.statusCode).json({
      error: error.message,
      code: error.code,
      type: 'authentication_error'
    });
  }

  if (error instanceof LicenseError) {
    return res.status(error.statusCode).json({
      error: error.message,
      feature: error.feature,
      upgrade_required: error.upgradeRequired,
      type: 'license_error'
    });
  }

  // Generic error
  console.error('Unhandled error:', error);
  return res.status(500).json({
    error: 'Internal server error',
    type: 'server_error'
  });
};

module.exports = { AuthError, LicenseError, errorHandler };

Token Refresh Edge Cases

// utils/tokenRefresh.js
class TokenRefreshManager {
  constructor() {
    this.refreshPromises = new Map();
  }

  /**
   * Handle concurrent refresh requests
   */
  async refreshToken(refreshToken) {
    // Prevent multiple simultaneous refresh requests
    if (this.refreshPromises.has(refreshToken)) {
      return this.refreshPromises.get(refreshToken);
    }

    const refreshPromise = this.performRefresh(refreshToken);
    this.refreshPromises.set(refreshToken, refreshPromise);

    try {
      const result = await refreshPromise;
      return result;
    } finally {
      this.refreshPromises.delete(refreshToken);
    }
  }

  async performRefresh(refreshToken) {
    try {
      const { data, error } = await supabase.auth.refreshSession({
        refresh_token: refreshToken
      });

      if (error) {
        throw new AuthError('Token refresh failed', 'REFRESH_FAILED');
      }

      // Verify subscription is still active
      const enrichedSession = await this.verifySubscriptionStatus(data.session);
      
      return enrichedSession;
    } catch (error) {
      if (error instanceof AuthError) {
        throw error;
      }
      throw new AuthError('Token refresh error', 'REFRESH_ERROR');
    }
  }

  async verifySubscriptionStatus(session) {
    const { data: workspace } = await supabase
      .from('workspaces')
      .select(`
        subscription:subscriptions(
          status,
          current_period_end
        )
      `)
      .eq('owner_id', session.user.id)
      .single();

    const subscription = workspace?.subscription;
    
    if (!subscription || subscription.status !== 'active') {
      throw new LicenseError('Subscription inactive', 'subscription_inactive');
    }

    if (new Date(subscription.current_period_end) < new Date()) {
      throw new LicenseError('Subscription expired', 'subscription_expired');
    }

    return session;
  }
}

module.exports = new TokenRefreshManager();

Monitoring & Analytics

Authentication Metrics

// utils/authMetrics.js
class AuthMetrics {
  static async trackLogin(userId, workspaceId, metadata = {}) {
    await supabase
      .from('auth_events')
      .insert({
        user_id: userId,
        workspace_id: workspaceId,
        event_type: 'login',
        metadata: {
          ip_address: metadata.ip,
          user_agent: metadata.userAgent,
          timestamp: new Date().toISOString()
        }
      });
  }

  static async trackTokenRefresh(userId, workspaceId) {
    await supabase
      .from('auth_events')
      .insert({
        user_id: userId,
        workspace_id: workspaceId,
        event_type: 'token_refresh',
        metadata: {
          timestamp: new Date().toISOString()
        }
      });
  }

  static async trackLicenseViolation(userId, workspaceId, feature, reason) {
    await supabase
      .from('auth_events')
      .insert({
        user_id: userId,
        workspace_id: workspaceId,
        event_type: 'license_violation',
        metadata: {
          feature,
          reason,
          timestamp: new Date().toISOString()
        }
      });
  }

  static async getAuthStats(workspaceId, days = 30) {
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);

    const { data } = await supabase
      .from('auth_events')
      .select('event_type, created_at')
      .eq('workspace_id', workspaceId)
      .gte('created_at', startDate.toISOString());

    return {
      totalLogins: data?.filter(e => e.event_type === 'login').length || 0,
      tokenRefreshes: data?.filter(e => e.event_type === 'token_refresh').length || 0,
      licenseViolations: data?.filter(e => e.event_type === 'license_violation').length || 0,
      dailyStats: this.groupByDay(data)
    };
  }

  static groupByDay(events) {
    const stats = {};
    events?.forEach(event => {
      const date = event.created_at.split('T')[0];
      if (!stats[date]) {
        stats[date] = { logins: 0, refreshes: 0, violations: 0 };
      }
      stats[date][event.event_type === 'login' ? 'logins' : 
                  event.event_type === 'token_refresh' ? 'refreshes' : 'violations']++;
    });
    return stats;
  }
}

module.exports = AuthMetrics;

Security Best Practices

Token Security Checklist

  • Secure Token Storage

    • Use httpOnly cookies for production
    • Implement CSRF protection
    • Never store tokens in localStorage in production
  • Token Rotation

    • Rotate tokens on suspicious activity
    • Implement token blacklisting
    • Use short-lived access tokens (15 minutes)
  • License Validation

    • Verify subscription status on every request
    • Implement real-time license updates
    • Track and alert on license violations
  • Rate Limiting

    • Implement auth endpoint rate limiting
    • Add progressive delays for failed attempts
    • Monitor for brute force attacks
  • Audit Logging

    • Log all authentication events
    • Track license violations
    • Monitor for anomalous patterns

Next Steps

Implementation Roadmap

  1. Phase 1: Basic Authentication (Week 1)

    • Set up Supabase Auth
    • Implement JWT middleware
    • Create user registration/login
  2. Phase 2: Licensing Integration (Week 2)

    • Database schema implementation
    • License validation middleware
    • Usage tracking system
  3. Phase 3: Advanced Features (Week 3)

    • Multi-workspace support
    • Role-based access control
    • Subscription management
  4. Phase 4: Security & Monitoring (Week 4)

    • Comprehensive audit logging
    • Security event monitoring
    • Performance optimization

Future Enhancements

  • Multi-Factor Authentication (MFA)
  • Single Sign-On (SSO) integration
  • Advanced usage analytics
  • Automated license compliance
  • Real-time subscription updates

This comprehensive guide provides the foundation for implementing secure, scalable JWT authentication with Supabase Auth and sophisticated licensing capabilities in Sasha Studio.