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:
- Custom Forms (
components/user/) - Built withuseSignInanduseSignUphooks for full UI control - Middleware (
middleware.ts) - Route protection usingclerkMiddlewareandauth.protect() - 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_...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>
<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 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
How is this guide ?
Last updated on