We use tracking cookies to understand how you use the product and help us improve it. For more information on how we store cookies, read our  privacy policy.

Overview

Clerk authentication integration in Plainform with custom flows, middleware protection, and OAuth support.

Plainform uses Clerk (v6.38+) for authentication, providing secure user management with minimal setup. The integration includes custom sign-in/sign-up forms, middleware-based route protection, and OAuth providers.

What is Clerk?

Clerk is a complete authentication and user management platform that handles:

  • Email/password authentication
  • OAuth providers (Google, GitHub, etc.)
  • Password reset and email verification
  • Session management
  • User profiles and metadata
  • Multi-factor authentication (2FA/OTP)
  • Passkeys (WebAuthn)

Clerk saves ~15 hours of development time by eliminating the need to build authentication flows from scratch.

How Plainform Uses Clerk

Plainform integrates Clerk at three key points:

  1. Custom Forms (components/user/) - Built with useSignIn and useSignUp hooks for full UI control
  2. Middleware (middleware.ts) - Route protection using clerkMiddleware and auth.protect()
  3. Server/Client Access - auth() in Server Components, useUser() in Client Components

For detailed information about how Clerk works internally, session management, token handling, and advanced features, see the official Clerk documentation.

Key Features in Plainform

Custom Authentication Forms

Plainform uses custom React components instead of Clerk's pre-built UI:

  • SignInForm - Email/password + OAuth buttons
  • SignUpForm - Registration with validation
  • ForgotPasswordForm - Password reset flow
  • OAuthConnection - Reusable OAuth button component

Why custom forms?

  • Full design control matching your brand
  • Custom validation with Zod schemas
  • Integrated with react-hook-form
  • Tailwind CSS styling

OAuth Providers

Pre-configured OAuth strategies:

  • Google - oauth_google
  • GitHub - oauth_github

Add more providers in Clerk Dashboard → Configure → Social Connections.

Middleware Protection

The proxy.ts middleware handles:

  • Redirect authenticated users - Signed-in users can't access /sign-in, /sign-up
  • Order verification - Validates Stripe session before showing order page
  • Route matching - Uses createRouteMatcher for pattern-based protection
proxy.ts
const isProtectedBySignInStatus = createRouteMatcher([
  '/sign-in(.*)',
  '/sign-up(.*)',
  '/forgot-password(.*)',
]);

export default clerkMiddleware(async (auth, req) => {
  const { userId } = await auth();
  
  // Redirect authenticated users away from auth pages
  if (userId && isProtectedBySignInStatus(req)) {
    return NextResponse.redirect(new URL('/', req.url));
  }
});

Server-Side Authentication

Access user data in Server Components:

Server Component Example
import { auth } from '@clerk/nextjs/server';

export default async function ProfilePage() {
  const { userId } = await auth();
  
  if (!userId) {
    redirect('/sign-in');
  }
  
  // Fetch user-specific data
}

Client-Side Hooks

Use Clerk hooks in Client Components:

Client Component Example
'use client';

import { useUser, useSignIn } from '@clerk/nextjs';

export function UserProfile() {
  const { user, isLoaded } = useUser();
  
  if (!isLoaded) return <Skeleton />;
  
  return <div>Welcome, {user?.firstName}!</div>;
}

Environment Variables

Required Clerk configuration in .env:

.env
# Public keys (client-side)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

# Secret key (server-side only)
CLERK_SECRET_KEY=sk_test_...

Never expose CLERK_SECRET_KEY in client-side code. Use NEXT_PUBLIC_* prefix only for publishable keys.

Authentication Architecture

Provider Setup

Clerk is initialized in the root layout via Providers.tsx:

components/providers/Providers.tsx
export async function Providers({ children }: IProviders) {
  return (
    <FumadocsProvider>
      <Suspense fallback={null}>
        <ClerkProvider>
          <ThemeProvider>{children}</ThemeProvider>
        </ClerkProvider>
      </Suspense>
    </FumadocsProvider>
  );
}

Why Suspense? - ClerkProvider accesses dynamic auth state, requiring Suspense boundary for Next.js 16 Cache Components mode.

Route Structure

app/
├── (auth)/              # Authentication pages
│   ├── sign-in/         # Custom sign-in form
│   ├── sign-up/         # Custom sign-up form
│   ├── forgot-password/ # Password reset
│   └── sso-callback/    # OAuth callback handler
└── (base)/              # Protected application routes

Validation with Zod

All auth forms use Zod schemas for type-safe validation:

validationSchemas/authSchemas.ts
export const signInSchema = z.object({
  identifier: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
});

Security Features

  • JWT tokens - Secure session management
  • HTTPS only - Cookies marked secure in production
  • CSRF protection - Built into Clerk SDK
  • Rate limiting - Automatic brute-force protection
  • Email verification - Required before account activation
  • Password requirements - Configurable in Clerk Dashboard

Performance Considerations

  • Server Components by default - Auth checks happen server-side
  • Minimal client JS - Only auth pages use 'use client'
  • Cached sessions - Clerk caches user data to reduce API calls
  • Lazy loading - OAuth buttons load on demand

How is this guide ?

Last updated on