Authentication & Role System

Autofocus implements a sophisticated authentication and authorization system with role-based permissions, subscription tiers, and granular access controls powered by Supabase Auth and PostgreSQL Row Level Security.

🔐 Authentication System

Supabase Auth Integration

Built on Supabase's battle-tested authentication with JWT tokens, OAuth providers, and automatic session management.

Supported Methods

  • • Email/Password authentication
  • • Google OAuth integration
  • • GitHub OAuth integration
  • • Email verification required
  • • Automatic session refresh

Security Features

  • • JWT token-based sessions
  • • Secure HTTP-only cookies
  • • Automatic token refresh
  • • Session timeout handling
  • • Rate limiting on auth endpoints

Authentication Flow

1. User signs up/logs in → Supabase Auth
2. JWT token issued → Stored in secure cookie
3. Profile created → Username setup required
4. Role assignment → Default 'basic' subscription
5. Dashboard access → Full platform functionality

👥 Role-Based Permission System

Role Types & Hierarchy

Three distinct role categories: permissions (admin access), subscriptions (feature access), and special roles (program participation).

Permission Roles

admin
Complete platform control
moderator
Content moderation access

Subscription Roles

basic
3 pages, 10 blocks
turbo
10 pages, 50 blocks
summit
Unlimited everything

Special Roles

alpha
Alpha program access
beta
Beta testing features
beta_plus
Enhanced beta access

Permission Matrix

PermissionAdminModeratorSummitTurboBasic
Admin Panel Access
User Management
Unlimited Pages
Custom Domains
Advanced Analytics

📊 Resource Limits & Subscription Tiers

Tier Comparison

FeatureBasic (Free)Turbo ($6.99/mo)Summit ($16.99/mo)
Pages310Unlimited
Blocks per Page1050Unlimited
Image Upload Size1MB5MB20MB
Custom Domain
AnalyticsBasicAdvancedPremium
Ad Revenue Share0%50%100%

Resource Limit Implementation

Server-Side Validation

// Check if user has reached limit
async function hasReachedLimit(userId, resourceType) {
  const { data: userSub } = await supabase
    .rpc('get_subscription_level', { user_id: userId });
  
  const { data: limits } = await supabase
    .from('resource_limits')
    .select('max_count')
    .eq('role_id', userSub.role_id)
    .eq('resource_type', resourceType)
    .single();
  
  if (limits.max_count === -1) return false; // Unlimited
  
  const { count } = await supabase
    .from(resourceType === 'page' ? 'pages' : 'content_blocks')
    .select('*', { count: 'exact' })
    .eq('profile_id', userId);
  
  return count >= limits.max_count;
}

Client-Side Enforcement

// React hook for subscription limits
const { subscriptionLevel, hasMinSubscription } = useAuthRoles();

const canCreatePage = useMemo(() => {
  if (hasMinSubscription('summit')) return true;
  
  return pageCount < getPageLimit(subscriptionLevel);
}, [subscriptionLevel, pageCount]);

// UI state management
if (!canCreatePage) {
  return <UpgradePrompt feature="additional pages" />;
}

🛡️ Row Level Security Implementation

Database-Level Security

PostgreSQL RLS policies ensure data security at the database level, preventing unauthorized access even if application logic fails.

User Data Isolation

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

-- Users can only modify their own pages
CREATE POLICY "Users can manage own pages" ON pages
  FOR ALL USING (profile_id = auth.uid());

-- Content blocks follow page ownership
CREATE POLICY "Users can manage own blocks" ON content_blocks
  FOR ALL USING (
    page_id IN (
      SELECT id FROM pages WHERE profile_id = auth.uid()
    )
  );

Public Content Access

-- Public profiles viewable by everyone
CREATE POLICY "Public profiles viewable" ON profiles
  FOR SELECT USING (true);

-- Public pages accessible to all
CREATE POLICY "Public pages viewable" ON pages
  FOR SELECT USING (is_public = true);

-- Public content blocks visible
CREATE POLICY "Public blocks viewable" ON content_blocks
  FOR SELECT USING (
    page_id IN (SELECT id FROM pages WHERE is_public = true)
  );

Admin Override Policies

-- Admins can access all data for moderation
CREATE POLICY "Admins can access all profiles" ON profiles
  FOR ALL USING (
    EXISTS (
      SELECT 1 FROM user_roles ur
      JOIN roles r ON ur.role_id = r.id
      WHERE ur.user_id = auth.uid() 
      AND r.name = 'admin'
    )
  );

⚛️ React Authentication Context

AuthRolesContext Implementation

React context providing authentication state, role information, and permission checking throughout the application.

Context Provider

export function AuthRolesProvider({ children }) {
  const [user, setUser] = useState(null);
  const [roles, setRoles] = useState([]);
  const [subscriptionLevel, setSubscriptionLevel] = useState('basic');
  const [loading, setLoading] = useState(true);

  // Check if user has specific role
  const hasRole = (roleName) => {
    if (roleName === 'admin' && user?.user_metadata?.is_admin) {
      return true;
    }
    return roles.some(role => role.role_name === roleName);
  };

  // Check subscription level
  const hasMinSubscription = (level) => {
    const levels = { basic: 0, turbo: 1, summit: 2 };
    return levels[subscriptionLevel] >= levels[level];
  };

  return (
    <AuthRolesContext.Provider value={{
      user, roles, subscriptionLevel, loading,
      hasRole, hasMinSubscription
    }}>
      {children}
    </AuthRolesContext.Provider>
  );
}

Role-Based Components

// Conditional rendering based on roles
function AdminPanel() {
  const { hasRole } = useAuthRoles();
  
  if (!hasRole('admin')) {
    return <NotAuthorized />;
  }
  
  return <AdminDashboard />;
}

// Subscription-gated features
function PremiumFeature() {
  const { hasMinSubscription } = useAuthRoles();
  
  if (!hasMinSubscription('turbo')) {
    return <UpgradePrompt />;
  }
  
  return <AdvancedAnalytics />;
}