# 🔐 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!** 💰