Prompt file imported from drewsephski/convext-workflow-orchestrator (
.windsurf/workflows/setup-auth-flow.md). Copyright stays with the author.
Authentication Setup Workflow
-
Configure Clerk Middleware Create/update
middleware.ts:import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'; const isPublicRoute = createRouteMatcher([ '/', '/sign-in(.*)', '/sign-up(.*)', '/api/webhooks(.*)', '/pricing', '/about', ]); export default clerkMiddleware(async (auth, req) => { if (!isPublicRoute(req)) { await auth.protect(); } }); -
Set Up Providers Update/create
app/providers.tsx:"use client"; import { ConvexReactClient } from 'convex/react'; import { ConvexProviderWithClerk } from 'convex/react-clerk'; import { ClerkProvider, useAuth } from '@clerk/nextjs'; const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); export function Providers({ children }: { children: React.ReactNode }) { return ( <ClerkProvider publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}> <ConvexProviderWithClerk client={convex} useAuth={useAuth}> {children} </ConvexProviderWithClerk> </ClerkProvider> ); } -
Update Root Layout Modify
app/layout.tsx:import Providers from "@/providers"; export default function RootLayout({ children }) { return ( <html lang="en"> <body> <Providers>{children}</Providers> </body> </html> ); } -
Create Auth Pages
mkdir -p app/(auth)/sign-in/[[...sign-in]] mkdir -p app/(auth)/sign-up/[[...sign-up]]Sign-in page (
app/(auth)/sign-in/[[...sign-in]]/page.tsx):import { SignIn } from '@clerk/nextjs'; export default function Page() { return <SignIn path="/sign-in" />; } -
Set Up Webhooks Create webhook handlers:
mkdir -p app/api/webhooks/clerk mkdir -p app/api/webhooks/stripeClerk webhook (
app/api/webhooks/clerk/route.ts):import { Webhook } from '@clerk/nextjs/server'; export const POST = Webhook({ secretKey: process.env.CLERK_WEBHOOK_SECRET!, async (event) => { switch (event.type) { case 'user.created': // Sync with Convex break; case 'session.created': // Handle new session break; } }, }); -
Environment Variables Add to
.env.local:# Clerk NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx CLERK_SECRET_KEY=sk_test_xxx CLERK_WEBHOOK_SECRET=whsec_xxx # Redirect URLs NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding -
Test Integration
# Test sign-up flow bun run dev # Navigate to http://localhost:3000/sign-up # Test sign-in flow # Navigate to http://localhost:3000/sign-in # Verify webhook delivery # Check Clerk Dashboard → Webhooks
Auth Patterns
Protected Routes
import { auth } from '@clerk/nextjs/server';
import { redirect } from 'next/navigation';
export default async function ProtectedPage() {
const { userId } = await auth();
if (!userId) redirect('/sign-in');
// Protected content
}
User Context Access
import { useAuth } from '@clerk/nextjs/react';
export function UserProfile() {
const { userId, isSignedIn } = useAuth();
if (!isSignedIn) {
return <div>Please sign in</div>;
}
// User is authenticated
}
Server-Side Auth
import { auth } from '@clerk/nextjs/server';
export async function getServerUser() {
const { userId } = await auth();
if (!userId) return null;
// Fetch user from Convex
const user = await fetchQuery(api.users.getByClerkId, { clerkId: userId });
return user;
}