Oliver Servín

Integrar Clerk en Next.js para autenticar usuarios.

Instalamos Clerk en nuestro proyecto.

npm install @clerk/nextjs

Nos registramos en Clerk y creamos un nuevo proyecto para nuestra aplicación Next.js.

Obtenemos las API Keys que nos proporciona Clerk y las pegamos en un archivo .env.local.

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_***
CLERK_SECRET_KEY=sk_test_***

Ahora creamos una ruta del tipo catch-all en Next.js creando un archivo app/(auth)/sign-up/[[...sign-up]]/page.tsx en la raíz de nuestro proyecto.

Lo mismo haremos para la ruta /sign-in creando el archivo app/(auth)/sign-in/[[...sign-in]]/page.tsx.

En el archivo app/(auth)/sign-up/[[...sign-up]]/page.tsx agregamos el componente SignUp de Clerk.

import { SignUp } from '@clerk/nextjs'
 
export default function Page() {
return <SignUp />
}

Y en el archivo app/(auth)/sign-in/[[...sign-in]]/page.tsx agregamos el componente SignIn de Clerk.

import { SignIn } from '@clerk/nextjs'
 
export default function Page() {
return <SignIn />
}

Ahora actualizamos nuestras variables en .env.local para agregar las rutas de inicio de sesión y registro.

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_***
CLERK_SECRET_KEY=sk_test_***
+NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
+NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

Actualizamos nuestro layout raíz para utilizar el componente ClerkProvider modificando el archivo app/layout.tsx.

import { ClerkProvider } from '@clerk/nextjs'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
 
const inter = Inter({ subsets: ['latin'] })
 
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
 
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<ClerkProvider afterSignOutUrl="/">
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
</ClerkProvider>
)
}

También tenemos que crear un archivo middleware.ts en la raíz de nuestro proyecto para comenzar a proteger rutas. Por ahora lo que haremos es proteger la ruta / con isProtectedRoute = createRouteMatcher(['/']).

import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'
 
const isProtectedRoute = createRouteMatcher(['/'])
 
export default clerkMiddleware((auth, request) => {
if (isProtectedRoute(request)) {
auth().protect()
}
 
return NextResponse.next()
})
 
export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
// Always run for API routes
'/(api|trpc)(.*)',
],
}

También movemos nuestra ruta raíz / para utilizar la agrupación de rutas de Next.js. Lo hacemos moviendo el archivo app/page.tsx a app/(dashboard)/page.tsx y cambiamos el contenido con lo siguiente:

export default function Home() {
return (
<div>
<p>Ruta protegida</p>
</div>
)
}

Ahora si accedemos a http://localhost:3000 nos debe redirigir a nuestra página de autenticación.

Podemos mejorar el diseño de nuestra página de autenticación.

Modificamos el archivo app/(auth)/sign-in/[[...sign-in]]/page.tsx con lo siguiente:

import { ClerkLoaded, ClerkLoading, SignIn } from '@clerk/nextjs'
import { Loader2 } from 'lucide-react'
import Image from 'next/image'
 
export default function Page() {
return (
<div className="grid min-h-screen grid-cols-1 lg:grid-cols-2">
<div className="h-full flex-col items-center justify-center px-4 lg:flex">
<div className="space-y-4 pt-16 text-center">
<h1 className="text-3xl font-bold text-[#2e2a47]">Bienvenido de nuevo</h1>
<p className="text-base text-[#7e8ca0]">Inicia sesión o crea una cuenta para volver a tu panel de control.</p>
</div>
<div className="mt-8 flex items-center justify-center">
<ClerkLoaded>
<SignIn />
</ClerkLoaded>
<ClerkLoading>
<Loader2 className="animate-spin text-muted-foreground" />
</ClerkLoading>
</div>
</div>
<div className="hidden h-full items-center justify-center bg-blue-600 lg:flex">
<Image src="/logo.svg" alt="Logo" width={100} height={100} />
</div>
</div>
)
}

El logo logo.svg es un logo de ejemplo que he tomado de Logoipsum y también hace uso de los componentes ClerkLoaded y ClerkLoading para mostrar un icono Loader2 de cargando, mientras se inicializa Clerk.

Lo mismo hacemos para la página de registro modificando el archivo app/(auth)/sign-up/[[...sign-up]]/page.tsx:

import { ClerkLoaded, ClerkLoading, SignUp } from '@clerk/nextjs'
import { Loader2 } from 'lucide-react'
import Image from 'next/image'
 
export default function Page() {
return (
<div className="grid min-h-screen grid-cols-1 lg:grid-cols-2">
<div className="h-full flex-col items-center justify-center px-4 lg:flex">
<div className="space-y-4 pt-16 text-center">
<h1 className="text-3xl font-bold text-[#2e2a47]">Bienvenido de nuevo</h1>
<p className="text-base text-[#7e8ca0]">Inicia sesión o crea una cuenta para volver a tu panel de control.</p>
</div>
<div className="mt-8 flex items-center justify-center">
<ClerkLoaded>
<SignUp />
</ClerkLoaded>
<ClerkLoading>
<Loader2 className="animate-spin text-muted-foreground" />
</ClerkLoading>
</div>
</div>
<div className="hidden h-full items-center justify-center bg-blue-600 lg:flex">
<Image src="/logo.svg" alt="Logo" width={100} height={100} />
</div>
</div>
)
}

Ahora tenemos nuestro sistema de autenticación implementado y podemos hacer un registro de prueba.

Una vez iniciada la sesión, podemos cerrarla utilizando el componente UserButton de Clerk.

Modificamos el archivo app/(dashboard)/page.tsx agregando lo siguiente:

import { UserButton } from '@clerk/nextjs'
 
export default function Home() {
return <UserButton />
}

Y nos mostrará un botón con opciones de nuestro perfil como cerrar la sesión.

UserButton