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.