Authentication Setup Guide
This guide will walk you through setting up Clerk.js authentication for Akako LMS.
Prerequisites
- Clerk.js account (sign up here)
- PostgreSQL database
- Node.js 18+ environment
Step 1: Create Clerk Application
-
Sign up for Clerk.js:
- Go to clerk.com
- Create a new account or sign in
-
Create a new application:
- Click "Create Application"
- Choose "Next.js" as your framework
- Select your preferred authentication methods (Email, Google, etc.)
-
Configure your application:
- Set your application name (e.g., "Akako LMS")
- Choose your preferred sign-in methods
- Configure user profile fields
Step 2: Get Clerk Credentials
After creating your application, you'll need these credentials:
-
Go to API Keys:
- Navigate to "API Keys" in your Clerk dashboard
- Copy the following keys:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYCLERK_SECRET_KEY
-
Get Webhook Endpoints (optional):
- Go to "Webhooks" section
- Create webhook endpoints for user synchronization
Step 3: Environment Configuration
Add the following environment variables to your .env.local file:
# Clerk.js Configuration
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
# Database
DATABASE_URL="postgresql://username:password@localhost:5432/akako"
# Application URLs
NEXT_PUBLIC_APP_URL=http://localhost:3000
Step 4: Install Dependencies
Install the required Clerk.js packages:
pnpm add @clerk/nextjs
Step 5: Configure Next.js
1. Update next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
images: {
domains: ['img.clerk.com'],
},
};
module.exports = nextConfig;
2. Create Middleware
Create middleware.ts in your project root:
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({
publicRoutes: [
"/",
"/api/webhooks(.*)",
"/api/auth/sync",
],
ignoredRoutes: [
"/api/webhooks/clerk",
],
});
export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
3. Wrap App with ClerkProvider
Update your root layout (app/layout.tsx):
import { ClerkProvider } from '@clerk/nextjs';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<ClerkProvider>
<html lang="en">
<body>{children}</body>
</html>
</ClerkProvider>
);
}
Step 6: Database Setup
1. Update Prisma Schema
Ensure your prisma/schema.prisma includes the Clerk integration:
model User {
id String @id @default(cuid())
clerkId String @unique // Clerk user ID
email String @unique
firstName String?
lastName String?
// ... other fields
userRoles UserRole[]
}
model UserRole {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role Role
effectiveStartDate DateTime @default(now())
effectiveEndDate DateTime?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum Role {
LEARNER
MENTOR
ADMIN
}
2. Run Database Migration
pnpm prisma generate
pnpm prisma db push
Step 7: Create Authentication Components
1. Sign In Component
Create components/auth/SignIn.tsx:
import { SignIn } from '@clerk/nextjs';
export default function SignInPage() {
return (
<div className="flex items-center justify-center min-h-screen">
<SignIn
appearance={{
elements: {
formButtonPrimary: 'bg-[#07294d] hover:bg-[#051f3a]',
card: 'shadow-lg',
},
}}
/>
</div>
);
}
2. Sign Up Component
Create components/auth/SignUp.tsx:
import { SignUp } from '@clerk/nextjs';
export default function SignUpPage() {
return (
<div className="flex items-center justify-center min-h-screen">
<SignUp
appearance={{
elements: {
formButtonPrimary: 'bg-[#07294d] hover:bg-[#051f3a]',
card: 'shadow-lg',
},
}}
/>
</div>
);
}
Step 8: User Synchronization
1. Create Sync API Route
Create app/api/auth/sync/route.ts:
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { Role } from '@prisma/client';
export async function POST(req: NextRequest) {
try {
const { userId, email, firstName, lastName } = await req.json();
const user = await prisma.user.upsert({
where: { clerkId: userId },
update: {
email,
firstName,
lastName,
},
create: {
clerkId: userId,
email,
firstName,
lastName,
userRoles: {
create: {
role: Role.LEARNER,
effectiveStartDate: new Date(),
},
},
},
});
return NextResponse.json(user);
} catch (error) {
console.error('User sync error:', error);
return NextResponse.json(
{ error: 'Failed to sync user' },
{ status: 500 }
);
}
}
2. Create User Profile Hook
Create lib/hooks/useAuth.ts:
import { useUser } from '@clerk/nextjs';
import { useQuery } from '@tanstack/react-query';
export function useAuth() {
const { user: clerkUser, isLoaded } = useUser();
const { data: user, isLoading } = useQuery({
queryKey: ['user', clerkUser?.id],
queryFn: async () => {
const response = await fetch('/api/user/profile');
if (!response.ok) throw new Error('Failed to fetch user');
return response.json();
},
enabled: isLoaded && !!clerkUser,
});
return {
user,
isLoading: isLoading || !isLoaded,
isAuthenticated: !!user,
roles: user?.roles || [],
};
}
Step 9: Test Authentication
1. Start Development Server
pnpm dev
2. Test Sign Up/Sign In
- Navigate to
http://localhost:3000 - Click "Sign Up" to create a new account
- Verify user is created in your database
- Test sign in functionality
3. Verify Database
Check your PostgreSQL database to ensure:
- User record is created with
clerkId - Default
LEARNERrole is assigned - User profile data is synced
-- Check users table
SELECT id, "clerkId", email, "firstName", "lastName" FROM users;
-- Check user roles
SELECT u.email, ur.role, ur."effectiveStartDate"
FROM users u
JOIN user_roles ur ON u.id = ur."userId";
Step 10: Configure Webhooks (Optional)
For automatic user synchronization, set up Clerk webhooks:
1. Create Webhook Endpoint
Create app/api/webhooks/clerk/route.ts:
import { NextRequest, NextResponse } from 'next/server';
import { Webhook } from 'svix';
import { prisma } from '@/lib/prisma';
import { Role } from '@prisma/client';
export async function POST(req: NextRequest) {
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
throw new Error('Missing CLERK_WEBHOOK_SECRET');
}
const payload = await req.text();
const headers = req.headers;
const wh = new Webhook(WEBHOOK_SECRET);
const evt = wh.verify(payload, {
'svix-id': headers.get('svix-id'),
'svix-timestamp': headers.get('svix-timestamp'),
'svix-signature': headers.get('svix-signature'),
});
const { type, data } = evt;
if (type === 'user.created') {
await prisma.user.create({
data: {
clerkId: data.id,
email: data.email_addresses[0].email_address,
firstName: data.first_name,
lastName: data.last_name,
userRoles: {
create: {
role: Role.LEARNER,
effectiveStartDate: new Date(),
},
},
},
});
}
return NextResponse.json({ received: true });
}
2. Add Webhook Secret to Environment
CLERK_WEBHOOK_SECRET=whsec_...
3. Configure Webhook in Clerk Dashboard
- Go to "Webhooks" in your Clerk dashboard
- Create a new webhook endpoint
- Set URL to:
https://yourdomain.com/api/webhooks/clerk - Select events:
user.created,user.updated,user.deleted
Troubleshooting
Common Issues
-
"Invalid Clerk Key" Error:
- Verify your environment variables are correct
- Ensure keys match your Clerk application
-
Database Connection Issues:
- Check your
DATABASE_URLformat - Ensure PostgreSQL is running
- Run
pnpm prisma generateafter schema changes
- Check your
-
User Not Syncing:
- Check webhook configuration
- Verify webhook secret is correct
- Check API route logs for errors
Debug Mode
Enable Clerk debug mode for development:
NEXT_PUBLIC_CLERK_DEBUG=true
Next Steps
- Role Management: Configure user roles and permissions
- API Protection: Protect API routes with role guards
- User Management: Manage user profiles and data