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:
- Custom Forms (
components/user/) - Built with Clerk hooks (useSignIn,useSignUp) for full UI control - Middleware (
proxy.ts) - Route protection usingclerkMiddlewarewith custom logic - 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 buttonsSignUpForm- Registration with validationForgotPasswordForm- Password reset flowOAuthConnection- 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
createRouteMatcherfor pattern-based protection
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:
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:
'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:
# 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:
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 routesValidation with Zod
All auth forms use Zod schemas for type-safe validation:
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
Related Resources
Setup & Configuration
Get your Clerk account configured and environment variables set up
Usage & Integration
Code examples for common authentication patterns
Clerk Webhook
Sync Clerk users into the database for payments and app data
Troubleshooting
Fix common authentication issues
Clerk Documentation
Official Clerk documentation and API reference
How is this guide ?
Last updated on