Overview

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

Plainform uses Clerk through the current @clerk/nextjs v6 integration for authentication, providing secure user management with minimal setup. The integration includes custom sign-in/sign-up forms built with Clerk's authentication hooks, middleware-based route protection, and OAuth providers.

Plainform has not upgraded to the Clerk Core 2 path Clerk support recommended because of an OAuth issue in the newer release path. Plainform stays on the current working Clerk integration for now, and we recommend that you do not upgrade to Clerk Core 3 until Clerk ships a fix for the OAuth bug. Once the issue is resolved, Plainform will upgrade and these examples will be updated.

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)

How Plainform Uses Clerk

Plainform integrates Clerk at three key points:

  1. Custom Forms (components/user/) - Built with Clerk hooks (useSignIn, useSignUp) for full UI control
  2. Middleware (proxy.ts) - Route protection using clerkMiddleware with custom logic
  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_...
CLERK_WEBHOOK_SECRET=whsec_...

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>
      <ClerkProvider>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </ClerkProvider>
    </FumadocsProvider>
  );
}

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

On this page