# 🔐 Authentication Setup Guide (Clerk Integration) Complete guide to adding authentication to AeThex Studio for monetization and user management. --- ## 🎯 Why Authentication? **Required for**: - User accounts and profiles - Feature gating by tier (Free/Studio/Pro) - Billing and subscriptions - Team collaboration - Usage tracking and analytics - API key management --- ## 🚀 Quick Start (30 minutes) ### Step 1: Create Clerk Account 1. Visit: https://clerk.com 2. Sign up (free tier: 10,000 MAU) 3. Create new application: "AeThex Studio" 4. Select authentication methods: - ✅ Email + Password - ✅ Google OAuth - ✅ GitHub OAuth - ⚠️ Magic Links (optional) ### Step 2: Install Dependencies ```bash npm install @clerk/nextjs ``` ### Step 3: Add Environment Variables ```bash # .env.local NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_... CLERK_SECRET_KEY=sk_test_... # Optional: Customize URLs NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/ NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/ ``` Get keys from: https://dashboard.clerk.com/apps → API Keys --- ## 📁 File Structure ``` src/ ├── app/ │ ├── sign-in/ │ │ └── [[...sign-in]]/ │ │ └── page.tsx │ ├── sign-up/ │ │ └── [[...sign-up]]/ │ │ └── page.tsx │ └── layout.tsx (wrap with ClerkProvider) ├── middleware.ts (protect routes) └── components/ └── UserButton.tsx (user menu) ``` --- ## 🛠️ Implementation ### 1. Update Next.js Layout **File**: `src/app/layout.tsx` ```typescript import { ClerkProvider } from '@clerk/nextjs'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` ### 2. Create Sign-In Page **File**: `src/app/sign-in/[[...sign-in]]/page.tsx` ```typescript import { SignIn } from '@clerk/nextjs'; export default function Page() { return (
); } ``` ### 3. Create Sign-Up Page **File**: `src/app/sign-up/[[...sign-up]]/page.tsx` ```typescript import { SignUp } from '@clerk/nextjs'; export default function Page() { return (
); } ``` ### 4. Add Middleware (Route Protection) **File**: `src/middleware.ts` ```typescript import { authMiddleware } from '@clerk/nextjs'; // Protect all routes except public ones export default authMiddleware({ publicRoutes: [ '/', '/sign-in(.*)', '/sign-up(.*)', '/api/public(.*)', ], }); export const config = { matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'], }; ``` ### 5. Add User Button to Toolbar **File**: `src/components/Toolbar.tsx` ```typescript import { UserButton, SignInButton, useUser } from '@clerk/nextjs'; export function Toolbar({ ... }: ToolbarProps) { const { isSignedIn, user } = useUser(); return (
{/* Existing toolbar items */} {/* Add authentication */} {isSignedIn ? (
{user.fullName || user.emailAddress}
) : ( )}
); } ``` --- ## 🎫 Feature Gating (Subscription Tiers) ### 1. Define User Tiers **File**: `src/lib/subscription-tiers.ts` ```typescript export type SubscriptionTier = 'free' | 'studio' | 'pro' | 'enterprise'; export interface TierFeatures { name: string; price: number; features: { translation: boolean; desktopApp: boolean; maxTemplates: number; teamCollaboration: boolean; prioritySupport: boolean; }; } export const tiers: Record = { free: { name: 'Foundation', price: 0, features: { translation: false, desktopApp: false, maxTemplates: 5, teamCollaboration: false, prioritySupport: false, }, }, studio: { name: 'Studio', price: 15, features: { translation: false, desktopApp: true, maxTemplates: -1, // unlimited teamCollaboration: false, prioritySupport: true, }, }, pro: { name: 'Pro', price: 45, features: { translation: true, // 🔥 KILLER FEATURE desktopApp: true, maxTemplates: -1, teamCollaboration: true, prioritySupport: true, }, }, enterprise: { name: 'Enterprise', price: 0, // Custom pricing features: { translation: true, desktopApp: true, maxTemplates: -1, teamCollaboration: true, prioritySupport: true, }, }, }; ``` ### 2. Add Tier to User Metadata In Clerk Dashboard: 1. Go to "Users" → "Metadata" 2. Add public metadata field: `subscriptionTier` 3. Default value: `"free"` ### 3. Check User Tier **File**: `src/lib/use-subscription.ts` ```typescript import { useUser } from '@clerk/nextjs'; import { tiers, SubscriptionTier } from './subscription-tiers'; export function useSubscription() { const { user } = useUser(); const tier: SubscriptionTier = (user?.publicMetadata?.subscriptionTier as SubscriptionTier) || 'free'; const features = tiers[tier].features; const hasFeature = (feature: keyof typeof features): boolean => { return features[feature] as boolean; }; const canUseTemplates = (count: number): boolean => { if (features.maxTemplates === -1) return true; return count <= features.maxTemplates; }; return { tier, features, hasFeature, canUseTemplates, isProOrHigher: tier === 'pro' || tier === 'enterprise', }; } ``` ### 4. Gate Translation Feature **File**: `src/components/Toolbar.tsx` ```typescript import { useSubscription } from '@/lib/use-subscription'; export function Toolbar({ ... }: ToolbarProps) { const { hasFeature, tier } = useSubscription(); const handleTranslateClick = () => { if (!hasFeature('translation')) { toast.error('Translation requires Pro plan. Upgrade to unlock!'); // Redirect to pricing page window.location.href = '/pricing'; return; } setShowTranslation(true); }; return ( ); } ``` ### 5. Gate Templates **File**: `src/components/TemplatesDrawer.tsx` ```typescript import { useSubscription } from '@/lib/use-subscription'; export function TemplatesDrawer({ ... }: TemplatesDrawerProps) { const { canUseTemplates, features, tier } = useSubscription(); const handleTemplateClick = (template: ScriptTemplate, index: number) => { // Check if user can access this template if (!canUseTemplates(index + 1)) { toast.error( `Template ${index + 1} requires ${features.maxTemplates < index + 1 ? 'Studio' : 'Free'} plan or higher` ); return; } onSelectTemplate(template.code); onClose(); }; return ( {/* ... */} {platformTemplates.map((template, index) => ( handleTemplateClick(template, index)} >

{template.name}

{!canUseTemplates(index + 1) && ( {tier === 'free' ? 'STUDIO' : 'PRO'} )}
{/* ... */}
))} ); } ``` --- ## 💳 Stripe Integration (Payments) ### 1. Install Stripe ```bash npm install @stripe/stripe-js stripe ``` ### 2. Add Environment Variables ```bash # .env.local NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_... STRIPE_SECRET_KEY=sk_test_... STRIPE_WEBHOOK_SECRET=whsec_... ``` ### 3. Create Pricing Page **File**: `src/app/pricing/page.tsx` ```typescript import { tiers } from '@/lib/subscription-tiers'; export default function PricingPage() { return (

Choose Your Plan

{Object.entries(tiers).map(([key, tier]) => (

{tier.name}

${tier.price} {tier.price > 0 && /mo}

  • ✓ {tier.features.maxTemplates === -1 ? 'Unlimited' : tier.features.maxTemplates} Templates
  • {tier.features.translation ? '✓' : '✗'} AI Translation
  • {tier.features.desktopApp ? '✓' : '✗'} Desktop App
  • {tier.features.teamCollaboration ? '✓' : '✗'} Team Collaboration
  • {tier.features.prioritySupport ? '✓' : '✗'} Priority Support
))}
); } ``` ### 4. Create Checkout API Route **File**: `src/app/api/create-checkout-session/route.ts` ```typescript import { NextResponse } from 'next/server'; import Stripe from 'stripe'; import { auth } from '@clerk/nextjs'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2023-10-16', }); export async function POST(req: Request) { const { userId } = auth(); if (!userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const { tier } = await req.json(); // Price IDs from Stripe Dashboard const priceIds = { studio: 'price_studio_monthly', pro: 'price_pro_monthly', }; const session = await stripe.checkout.sessions.create({ customer_email: userId, // Or get from Clerk line_items: [ { price: priceIds[tier as keyof typeof priceIds], quantity: 1, }, ], mode: 'subscription', success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?success=true`, cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing?canceled=true`, metadata: { userId, tier, }, }); return NextResponse.json({ url: session.url }); } ``` ### 5. Handle Webhooks **File**: `src/app/api/webhooks/stripe/route.ts` ```typescript import { NextResponse } from 'next/server'; import Stripe from 'stripe'; import { clerkClient } from '@clerk/nextjs'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2023-10-16', }); export async function POST(req: Request) { const body = await req.text(); const sig = req.headers.get('stripe-signature')!; let event: Stripe.Event; try { event = stripe.webhooks.constructEvent( body, sig, process.env.STRIPE_WEBHOOK_SECRET! ); } catch (err) { return NextResponse.json({ error: 'Webhook error' }, { status: 400 }); } // Handle successful payment if (event.type === 'checkout.session.completed') { const session = event.data.object as Stripe.Checkout.Session; const userId = session.metadata?.userId; const tier = session.metadata?.tier; if (userId && tier) { // Update user's tier in Clerk await clerkClient.users.updateUserMetadata(userId, { publicMetadata: { subscriptionTier: tier, stripeCustomerId: session.customer, }, }); } } // Handle subscription cancellation if (event.type === 'customer.subscription.deleted') { const subscription = event.data.object as Stripe.Subscription; // Downgrade user to free tier } return NextResponse.json({ received: true }); } ``` --- ## 📊 Usage Tracking (Optional) ### Track API Usage Per User **File**: `src/lib/usage-tracking.ts` ```typescript import { auth } from '@clerk/nextjs'; import { createClient } from '@supabase/supabase-js'; const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY! ); export async function trackTranslation( sourcePlatform: string, targetPlatform: string ) { const { userId } = auth(); if (!userId) return; await supabase.from('usage').insert({ user_id: userId, action: 'translation', source_platform: sourcePlatform, target_platform: targetPlatform, timestamp: new Date().toISOString(), }); } export async function getUserUsage(userId: string, month: string) { const { data } = await supabase .from('usage') .select('*') .eq('user_id', userId) .gte('timestamp', `${month}-01`) .lte('timestamp', `${month}-31`); return { translationCount: data?.filter(u => u.action === 'translation').length || 0, }; } ``` --- ## ✅ Implementation Checklist ### Phase 1: Basic Auth (Week 1) - [ ] Install Clerk - [ ] Add environment variables - [ ] Create sign-in/sign-up pages - [ ] Add middleware - [ ] Add UserButton to Toolbar - [ ] Test authentication flow ### Phase 2: Feature Gating (Week 2) - [ ] Define subscription tiers - [ ] Create `use-subscription` hook - [ ] Gate translation feature - [ ] Gate templates (5 free, rest require Studio+) - [ ] Add upgrade prompts - [ ] Create pricing page ### Phase 3: Payments (Week 3) - [ ] Install Stripe - [ ] Create products in Stripe Dashboard - [ ] Implement checkout API - [ ] Add webhook handler - [ ] Test payment flow - [ ] Handle subscription management ### Phase 4: Polish (Week 4) - [ ] Add usage tracking - [ ] Create user dashboard - [ ] Implement billing portal - [ ] Add team features (Pro tier) - [ ] Test edge cases - [ ] Deploy to production --- ## 🎯 Quick Win: Free vs Pro **Easiest monetization path**: 1. **Free Tier**: - 5 templates (1 per category) - No translation (show "Upgrade to Pro" message) - Web IDE only 2. **Pro Tier ($45/mo)**: - ✅ **AI Translation** (killer feature) - ✅ All 43 templates - ✅ Desktop app access - ✅ Priority support **Implementation**: Just gate translation feature. That alone justifies $45/mo for studios. --- ## 📚 Resources - **Clerk Docs**: https://clerk.com/docs - **Stripe Docs**: https://stripe.com/docs - **Next.js Auth**: https://clerk.com/docs/quickstarts/nextjs - **Webhooks**: https://stripe.com/docs/webhooks --- **Ready to monetize? Start with Clerk auth, then add Stripe payments!** 💰