Cours Next.js · Module 7: Authentification et autorisation
Implémenter Clerk dans Next.js
Dans cette leçon11
Dans les applications modernes, une gestion fiable et sécurisée de l'authentification et des utilisateurs est le fondement d'une bonne expérience utilisateur. Clerk est une solution complète qui simplifie cet aspect du développement, en proposant des composants prêts à l'emploi et une API pour gérer l'identité des utilisateurs dans les applications Next.js.
Qu'est-ce que Clerk ?
Clerk est une plateforme complète de gestion des identités (Identity-as-a-Service), conçue spécialement pour les applications Next.js et React. Contrairement à d'autres solutions, Clerk propose une boîte à outils complète pour l'authentification, la gestion des utilisateurs et la gestion des sessions.
Les principaux avantages de Clerk :
- Des composants UI prêts à l'emploi - des composants déjà stylisés pour la connexion, l'inscription, la vérification en deux étapes et plus encore
- Support natif pour Next.js App Router - optimisé pour fonctionner avec les dernières versions de Next.js
- Support multiplateforme - prise en charge de nombreux appareils et plateformes
- Authentification multifacteur - prise en charge intégrée de la 2FA
- Gestion des organisations et des équipes - des fonctionnalités pour les applications B2B
- Conformité au RGPD et à d'autres réglementations - des mécanismes intégrés de protection de la vie privée
Installation et configuration
Commençons par installer les paquets Clerk dans un projet Next.js :
1npm install @clerk/nextjs
2# ou
3yarn add @clerk/nextjs
4# ou
5pnpm add @clerk/nextjsConfiguration des clés API
Après avoir créé un compte sur clerk.com et une nouvelle application, tu recevras des clés API. Ajoute-les aux variables d'environnement dans le fichier .env.local :
1NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
2CLERK_SECRET_KEY=sk_test_...Si tu utilises GitHub ou un autre système de gestion de versions, assure-toi que le fichier .env.local est bien ajouté au .gitignore.
Envelopper l'application dans le provider Clerk
Pour que Clerk fonctionne dans toute l'application, nous devons ajouter ClerkProvider dans le layout principal :
1// app/layout.tsx
2import { ClerkProvider } from '@clerk/nextjs';
3
4export default function RootLayout({
5 children,
6}: {
7 children: React.ReactNode;
8}) {
9 return (
10 <ClerkProvider>
11 <html lang="pl">
12 <body>{children}</body>
13 </html>
14 </ClerkProvider>
15 );
16}Configuration des chemins publics et protégés
Une étape importante consiste à déterminer quels chemins sont publics (accessibles aux utilisateurs non connectés) et lesquels sont protégés (nécessitant une connexion). On peut le faire à l'aide d'un middleware :
1// middleware.ts
2import { authMiddleware } from "@clerk/nextjs";
3
4export default authMiddleware({
5 publicRoutes: ["/", "/contact", "/about", "/api/public"],
6});
7
8export const config = {
9 matcher: ["/((?!.+\.[\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
10};Dans l'exemple ci-dessus, la page d'accueil, la page de contact et la page « à propos » sont publiques, et toutes les autres nécessitent une connexion.
Composants d'authentification
Clerk fournit des composants prêts à l'emploi pour les opérations d'authentification les plus courantes.
SignIn et SignUp
Mettre en place les pages de connexion et d'inscription est très simple :
1// app/sign-in/[[...sign-in]]/page.tsx
2import { SignIn } from "@clerk/nextjs";
3
4export default function SignInPage() {
5 return (
6 <div className="flex justify-center items-center min-h-screen">
7 <SignIn
8 appearance={{
9 elements: {
10 formButtonPrimary: "bg-blue-500 hover:bg-blue-600 text-white",
11 footerActionLink: "text-blue-500 hover:text-blue-600"
12 }
13 }}
14 />
15 </div>
16 );
17}
18
19// app/sign-up/[[...sign-up]]/page.tsx
20import { SignUp } from "@clerk/nextjs";
21
22export default function SignUpPage() {
23 return (
24 <div className="flex justify-center items-center min-h-screen">
25 <SignUp
26 appearance={{
27 elements: {
28 formButtonPrimary: "bg-blue-500 hover:bg-blue-600 text-white",
29 footerActionLink: "text-blue-500 hover:text-blue-600"
30 }
31 }}
32 />
33 </div>
34 );
35}Remarque le motif [[...sign-in]] dans le nom du dossier : les doubles crochets désignent un paramètre catch-all optionnel, ce qui permet à Clerk de gérer les différents états de l'authentification.
UserButton - le bouton de profil utilisateur
Ajoute à l'en-tête un bouton de profil utilisateur qui affiche un menu avec les options de gestion du compte :
1// components/Header.tsx
2import { UserButton } from "@clerk/nextjs";
3
4export function Header() {
5 return (
6 <header className="flex justify-between items-center p-4 bg-white shadow">
7 <div className="font-bold text-xl">Mon application</div>
8 <UserButton afterSignOutUrl="/" />
9 </header>
10 );
11}Accéder aux informations de l'utilisateur
Dans les composants côté client, on peut utiliser les hooks de Clerk pour obtenir des informations sur l'utilisateur connecté :
1// components/Profile.tsx
2'use client';
3
4import { useUser } from "@clerk/nextjs";
5
6export function Profile() {
7 const { user, isLoaded } = useUser();
8
9 if (!isLoaded) {
10 return <div>Chargement...</div>;
11 }
12
13 return (
14 <div className="p-4">
15 <h1 className="text-2xl font-bold mb-4">Ton profil</h1>
16 <div className="mb-4">
17 <img
18 src={user?.imageUrl}
19 alt="Photo de profil"
20 className="w-16 h-16 rounded-full"
21 />
22 </div>
23 <p><strong>Prénom :</strong> {user?.firstName}</p>
24 <p><strong>Nom :</strong> {user?.lastName}</p>
25 <p><strong>Email :</strong> {user?.primaryEmailAddress?.emailAddress}</p>
26 </div>
27 );
28}Dans les composants côté serveur, on peut utiliser des fonctions utilitaires :
1// app/dashboard/page.tsx
2import { currentUser } from "@clerk/nextjs";
3
4export default async function DashboardPage() {
5 const user = await currentUser();
6
7 if (!user) {
8 return <div>Tu n'es pas connecté</div>;
9 }
10
11 return (
12 <div className="p-4">
13 <h1 className="text-2xl font-bold mb-4">Espace utilisateur</h1>
14 <p>Bonjour, {user.firstName} ! Voici ton espace utilisateur.</p>
15 </div>
16 );
17}Gestion des organisations
L'un des points forts de Clerk est la prise en charge intégrée des organisations (équipes), particulièrement utile dans les applications B2B.
Composant de sélection d'organisation
1// components/OrganizationSwitcher.tsx
2import { OrganizationSwitcher } from "@clerk/nextjs";
3
4export function OrgSwitcher() {
5 return (
6 <OrganizationSwitcher
7 appearance={{
8 elements: {
9 organizationSwitcherTrigger: "p-2 border rounded-md"
10 }
11 }}
12 />
13 );
14}Créer une nouvelle organisation
1// app/organizations/new/page.tsx
2import { CreateOrganization } from "@clerk/nextjs";
3
4export default function NewOrganizationPage() {
5 return (
6 <div className="flex justify-center items-center min-h-screen">
7 <CreateOrganization
8 appearance={{
9 elements: {
10 formButtonPrimary: "bg-blue-500 hover:bg-blue-600 text-white",
11 }
12 }}
13 />
14 </div>
15 );
16}Accéder aux données de l'organisation
1// app/dashboard/organization/page.tsx
2import { currentUser, clerkClient } from "@clerk/nextjs";
3import { redirect } from "next/navigation";
4
5export default async function OrganizationDashboard() {
6 const user = await currentUser();
7
8 if (!user) {
9 return redirect("/sign-in");
10 }
11
12 // Récupère l'organisation active
13 const org = await clerkClient.organizations.getOrganization({
14 organizationId: user.organizationId || ""
15 });
16
17 if (!org) {
18 return (
19 <div className="p-4">
20 <h1 className="text-2xl font-bold mb-4">Aucune organisation sélectionnée</h1>
21 <p>Sélectionne une organisation pour voir son tableau de bord.</p>
22 </div>
23 );
24 }
25
26 return (
27 <div className="p-4">
28 <h1 className="text-2xl font-bold mb-4">Tableau de bord de l'organisation : {org.name}</h1>
29 <p>Membres : {org.membersCount}</p>
30 <p>Créé le : {new Date(org.createdAt).toLocaleDateString()}</p>
31 </div>
32 );
33}Sécuriser les API Routes
Dans l'App Router de Next.js, on peut facilement sécuriser les routes API grâce aux fonctions utilitaires de Clerk :
1// app/api/protected/route.ts
2import { auth } from "@clerk/nextjs";
3import { NextResponse } from "next/server";
4
5export async function GET() {
6 const { userId } = auth();
7
8 if (!userId) {
9 return new NextResponse(JSON.stringify({ error: "Unauthorized" }), {
10 status: 401,
11 headers: { "Content-Type": "application/json" }
12 });
13 }
14
15 return NextResponse.json({
16 message: "Ce sont des données protégées réservées aux utilisateurs connectés"
17 });
18}Personnalisation de l'apparence
Clerk propose de nombreuses options de personnalisation de l'apparence. On peut le faire de trois façons :
1. Avec la prop appearance
1import { SignIn } from "@clerk/nextjs";
2
3export default function SignInPage() {
4 return (
5 <SignIn
6 appearance={{
7 elements: {
8 formButtonPrimary: "bg-gradient-to-r from-blue-500 to-purple-500 text-white",
9 card: "shadow-xl border-0",
10 socialButtonsBlockButton: "border border-gray-300 hover:bg-gray-100"
11 }
12 }}
13 />
14 );
15}2. Avec le provider d'apparence
1// app/layout.tsx
2import { ClerkProvider } from '@clerk/nextjs';
3
4const appearance = {
5 elements: {
6 formButtonPrimary: "bg-blue-500 hover:bg-blue-600 text-white",
7 footerActionLink: "text-blue-500 hover:text-blue-600",
8 card: "shadow-md"
9 },
10 variables: {
11 colorPrimary: "#3B82F6"
12 }
13};
14
15export default function RootLayout({
16 children,
17}: {
18 children: React.ReactNode;
19}) {
20 return (
21 <ClerkProvider appearance={appearance}>
22 <html lang="pl">
23 <body>{children}</body>
24 </html>
25 </ClerkProvider>
26 );
27}3. Avec des styles CSS personnalisés
Tu peux aussi créer un fichier CSS avec tes propres styles :
1/* styles/clerk.css */
2.cl-card {
3 border-radius: 12px;
4 box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
5}
6
7.cl-formButtonPrimary {
8 background: linear-gradient(45deg, #3B82F6, #6366F1);
9 border-radius: 6px;
10 transition: transform 0.2s;
11}
12
13.cl-formButtonPrimary:hover {
14 transform: translateY(-2px);
15}Puis l'importer dans l'application :
1// app/layout.tsx
2import "../styles/clerk.css";Webhooks et intégration avec la base de données
Dans la plupart des applications, tu voudras synchroniser les données des utilisateurs Clerk avec ta propre base de données. Le meilleur moyen d'y parvenir est d'utiliser des webhooks :
Configuration de l'endpoint webhook
1// app/api/webhooks/clerk/route.ts
2import { WebhookEvent } from "@clerk/nextjs/server";
3import { headers } from "next/headers";
4import { NextResponse } from "next/server";
5import { Webhook } from "svix";
6
7export async function POST(req: Request) {
8 const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET;
9
10 if (!WEBHOOK_SECRET) {
11 throw new Error('CLERK_WEBHOOK_SECRET manquant');
12 }
13
14 // Récupère les en-têtes
15 const headerPayload = await headers();
16 const svix_id = headerPayload.get("svix-id");
17 const svix_timestamp = headerPayload.get("svix-timestamp");
18 const svix_signature = headerPayload.get("svix-signature");
19
20 if (!svix_id || !svix_timestamp || !svix_signature) {
21 return new NextResponse(JSON.stringify({ error: "En-têtes requis manquants" }), {
22 status: 400
23 });
24 }
25
26 // Récupère le corps de la requête
27 const payload = await req.json();
28 const body = JSON.stringify(payload);
29
30 // Vérifie le webhook
31 const webhook = new Webhook(WEBHOOK_SECRET);
32 let event: WebhookEvent;
33
34 try {
35 event = webhook.verify(body, {
36 "svix-id": svix_id,
37 "svix-timestamp": svix_timestamp,
38 "svix-signature": svix_signature,
39 }) as WebhookEvent;
40 } catch (err) {
41 console.error('Erreur de vérification du webhook :', err);
42 return new NextResponse(JSON.stringify({ error: "Signature invalide" }), {
43 status: 400
44 });
45 }
46
47 // Gestion des différents types d'événements
48 const { type } = event;
49
50 switch (type) {
51 case 'user.created': {
52 const { id, email_addresses, username, first_name, last_name } = event.data;
53 const primaryEmail = email_addresses?.[0]?.email_address;
54
55 // Ajoute ici la logique d'enregistrement de l'utilisateur dans la base de données
56 // Par exemple avec Prisma :
57 // await prisma.user.create({
58 // data: {
59 // clerkId: id,
60 // email: primaryEmail,
61 // username: username || "",
62 // firstName: first_name || "",
63 // lastName: last_name || ""
64 // }
65 // });
66
67 break;
68 }
69
70 case 'user.updated': {
71 const { id, email_addresses, username, first_name, last_name } = event.data;
72 const primaryEmail = email_addresses?.[0]?.email_address;
73
74 // Ici, mets à jour l'utilisateur dans la base de données
75 break;
76 }
77
78 case 'user.deleted': {
79 const { id } = event.data;
80
81 // Ici, supprime l'utilisateur de la base de données
82 break;
83 }
84
85 // Gère les autres types d'événements, comme organization.created, organization.updated, etc.
86 }
87
88 return NextResponse.json({ message: "Webhook reçu" }, { status: 200 });
89}Fonctionnalités avancées
Authentification multifacteur (2FA)
Clerk propose une prise en charge intégrée de la 2FA. Tu peux activer cette fonctionnalité dans le panneau d'administration de Clerk.
Connexion sans mot de passe
Tu peux configurer une connexion par magic links (sans mot de passe) :
1// app/sign-in/magic-link/page.tsx
2import { SignIn } from "@clerk/nextjs";
3
4export default function MagicLinkPage() {
5 return (
6 <div className="flex justify-center items-center min-h-screen">
7 <SignIn path="/sign-in/magic-link" />
8 </div>
9 );
10}Connexion en plusieurs étapes
Tu peux créer un processus de connexion personnalisé en plusieurs étapes :
1// components/CustomSignIn.tsx
2'use client';
3
4import { useSignIn } from "@clerk/nextjs";
5import { useState } from "react";
6
7export function CustomSignIn() {
8 const { isLoaded, signIn, setActive } = useSignIn();
9 const [email, setEmail] = useState("");
10 const [password, setPassword] = useState("");
11 const [verificationCode, setVerificationCode] = useState("");
12 const [stage, setStage] = useState("email");
13 const [error, setError] = useState("");
14
15 if (!isLoaded) {
16 return <div>Chargement...</div>;
17 }
18
19 async function handleSubmitEmail(e: React.FormEvent) {
20 e.preventDefault();
21 setError("");
22
23 try {
24 await signIn.create({
25 identifier: email
26 });
27
28 await signIn.prepareFirstFactor({
29 strategy: "email_code",
30 email
31 });
32
33 setStage("verification");
34 } catch (err: any) {
35 setError(err.errors?.[0]?.message || "Une erreur est survenue");
36 }
37 }
38
39 async function handleVerifyCode(e: React.FormEvent) {
40 e.preventDefault();
41 setError("");
42
43 try {
44 const result = await signIn.attemptFirstFactor({
45 strategy: "email_code",
46 code: verificationCode
47 });
48
49 if (result.status === "complete") {
50 await setActive({ session: result.createdSessionId });
51 // Redirection après la connexion
52 window.location.href = "/dashboard";
53 }
54 } catch (err: any) {
55 setError(err.errors?.[0]?.message || "Code invalide");
56 }
57 }
58
59 return (
60 <div className="p-4 max-w-sm mx-auto">
61 <h1 className="text-2xl font-bold mb-4 text-center">Connexion</h1>
62
63 {error && (
64 <div className="mb-4 p-2 bg-red-100 text-red-700 rounded-md">
65 {error}
66 </div>
67 )}
68
69 {stage === "email" ? (
70 <form onSubmit={handleSubmitEmail} className="space-y-4">
71 <div>
72 <label htmlFor="email" className="block mb-1">Email</label>
73 <input
74 id="email"
75 type="email"
76 value={email}
77 onChange={(e) => setEmail(e.target.value)}
78 required
79 className="w-full p-2 border rounded-md"
80 />
81 </div>
82
83 <button
84 type="submit"
85 className="w-full bg-blue-500 text-white p-2 rounded-md hover:bg-blue-600"
86 >
87 Continuer
88 </button>
89 </form>
90 ) : (
91 <form onSubmit={handleVerifyCode} className="space-y-4">
92 <div>
93 <label htmlFor="code" className="block mb-1">Code de vérification</label>
94 <input
95 id="code"
96 type="text"
97 value={verificationCode}
98 onChange={(e) => setVerificationCode(e.target.value)}
99 required
100 className="w-full p-2 border rounded-md"
101 />
102 </div>
103
104 <button
105 type="submit"
106 className="w-full bg-blue-500 text-white p-2 rounded-md hover:bg-blue-600"
107 >
108 Vérifier
109 </button>
110
111 <button
112 type="button"
113 className="w-full text-blue-500 hover:text-blue-600"
114 onClick={() => setStage("email")}
115 >
116 Retour
117 </button>
118 </form>
119 )}
120 </div>
121 );
122}Inconvénients et limites de Clerk
Même si Clerk offre de nombreux avantages, il vaut mieux connaître certaines de ses limites :
- Le coût - contrairement à Auth.js, Clerk fonctionne sur un modèle freemium et exige un abonnement payant pour les applications plus importantes
- La dépendance à un service externe - les données d'authentification sont stockées sur les serveurs de Clerk
- Moins de contrôle - tu as moins de contrôle sur les détails de l'implémentation de l'authentification
- La personnalisation - malgré de nombreuses options, une solution native peut parfois offrir plus de liberté
Quand choisir Clerk plutôt qu'Auth.js ?
Pour prendre ta décision, tiens compte des facteurs suivants :
Choisis Clerk quand :
- Tu as besoin d'une solution d'authentification complète et prête à l'emploi
- Tu veux mettre en place rapidement l'authentification avec un effort minimal
- Tu as besoin de gérer des organisations et des équipes
- Tu as besoin de fonctionnalités avancées comme 2FA, magic links, etc.
- Tu ne veux pas gérer toi-même l'infrastructure d'authentification
Choisis Auth.js quand :
- Tu as besoin de plus de contrôle sur le processus d'authentification
- Tu préfères une solution open source
- Tu ne veux pas dépendre d'un service externe
- Tu disposes d'un budget limité pour un projet plus important
- Tu dois stocker les données d'authentification dans ta propre base de données
Récapitulatif
Clerk est un outil puissant qui peut considérablement accélérer la mise en place de l'authentification dans les applications Next.js. Il offre un riche ensemble de fonctionnalités qui demanderaient des semaines de travail si tu devais les implémenter toi-même. Pour de nombreuses équipes de développement, le temps gagné et les solutions de sécurité prêtes à l'emploi l'emportent sur le coût et la perte de contrôle.
Cependant, le choix entre Clerk, Auth.js ou une solution d'authentification maison doit reposer sur les exigences concrètes du projet, le budget disponible et les besoins métier.
Code de cette leçon : App.tsx
1import React, { useState } from 'react';
2
3// Démo : Clerk dans Next.js
4interface ClerkComponent {
5 name: string; icon: string; description: string; code: string;
6 category: 'ui' | 'hook' | 'middleware' | 'config';
7}
8
9const components: ClerkComponent[] = [
10 { name: '<SignIn />', icon: 'K', category: 'ui',
11 description: 'Composant de connexion prêt à l\'emploi avec OAuth, email et mot de passe',
12 code: "import { SignIn } from '@clerk/nextjs';\n\nexport default function SignInPage() {\n return <SignIn redirectUrl=\"/dashboard\" />;\n}" },
13 { name: '<SignUp />', icon: 'R', category: 'ui',
14 description: 'Composant d\'inscription avec vérification par email',
15 code: "import { SignUp } from '@clerk/nextjs';\n\nexport default function SignUpPage() {\n return <SignUp />;\n}" },
16 { name: '<UserButton />', icon: 'U', category: 'ui',
17 description: 'Avatar avec menu (profil, déconnexion)',
18 code: "import { UserButton } from '@clerk/nextjs';\n\nfunction Navbar() {\n return <UserButton afterSignOutUrl=\"/\" />;\n}" },
19 { name: 'useUser()', icon: 'H', category: 'hook',
20 description: 'Hook pour les données de l\'utilisateur connecté',
21 code: "import { useUser } from '@clerk/nextjs';\n\nfunction Profile() {\n const { user, isLoaded, isSignedIn } = useUser();\n if (!isLoaded) return <div>Loading...</div>;\n return <h1>Welcome, {user.firstName}!</h1>;\n}" },
22 { name: 'useAuth()', icon: 'A', category: 'hook',
23 description: 'Hook pour l\'état auth et les tokens',
24 code: "import { useAuth } from '@clerk/nextjs';\n\nfunction ApiCall() {\n const { getToken, userId } = useAuth();\n // const token = await getToken();\n}" },
25 { name: 'clerkMiddleware()', icon: 'M', category: 'middleware',
26 description: 'Middleware pour protéger les routes sur l\'edge',
27 code: "import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';\n\nconst isProtected = createRouteMatcher(['/dashboard(.*)']);\n\nexport default clerkMiddleware((auth, req) => {\n if (isProtected(req)) auth().protect();\n});" },
28 { name: 'ClerkProvider', icon: 'P', category: 'config',
29 description: 'Provider englobant l\'application',
30 code: "import { ClerkProvider } from '@clerk/nextjs';\n\nexport default function RootLayout({ children }) {\n return (\n <ClerkProvider>\n <html><body>{children}</body></html>\n </ClerkProvider>\n );\n}" },
31];
32
33const catColors: Record<string, string> = { ui: '#3b82f6', hook: '#8b5cf6', middleware: '#f59e0b', config: '#10b981' };
34
35function ClerkDemo() {
36 const [selected, setSelected] = useState(components[0]);
37 const [filter, setFilter] = useState('all');
38 const [setupStep, setSetupStep] = useState(0);
39
40 const setupSteps = [
41 { title: 'Installation', cmd: 'npm install @clerk/nextjs' },
42 { title: 'Env vars', cmd: 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...\nCLERK_SECRET_KEY=sk_...' },
43 { title: 'Provider', cmd: 'Enveloppe <html> dans <ClerkProvider>' },
44 { title: 'Middleware', cmd: 'Crée middleware.ts avec clerkMiddleware()' },
45 { title: 'Terminé !', cmd: 'Utilise <SignIn />, <UserButton />, useUser()' },
46 ];
47
48 const filtered = filter === 'all' ? components : components.filter(c => c.category === filter);
49
50 return (
51 <div style={{ background: '#0f0f23', minHeight: '100vh', padding: '20px', color: '#e0e0e0', fontFamily: 'system-ui' }}>
52 <h1 style={{ color: '#64ffda', marginBottom: '8px' }}>Clerk - Auth dans Next.js</h1>
53 <p style={{ color: '#8892b0', marginBottom: '24px' }}>Composants et hooks prêts à l'emploi pour l'authentification</p>
54
55 <div style={{ background: '#1a1a2e', padding: '16px', borderRadius: '12px', border: '1px solid #333', marginBottom: '24px' }}>
56 <h3 style={{ color: '#64ffda', marginTop: 0, fontSize: '16px' }}>Setup</h3>
57 <div style={{ display: 'flex', gap: '8px', marginBottom: '12px', flexWrap: 'wrap' }}>
58 {setupSteps.map((s, i) => (
59 <button key={i} onClick={() => setSetupStep(i)} style={{
60 padding: '6px 12px', borderRadius: '6px', border: 'none', cursor: 'pointer', fontSize: '12px',
61 background: setupStep === i ? '#64ffda' : i < setupStep ? '#10b98130' : '#333',
62 color: setupStep === i ? '#0f0f23' : '#8892b0',
63 }}>{i + 1}. {s.title}</button>
64 ))}
65 </div>
66 <pre style={{ background: '#0f0f23', padding: '12px', borderRadius: '6px', fontSize: '13px', color: '#f59e0b', margin: 0, whiteSpace: 'pre-wrap' }}>{setupSteps[setupStep].cmd}</pre>
67 </div>
68
69 <div style={{ display: 'flex', gap: '8px', marginBottom: '20px' }}>
70 {['all', 'ui', 'hook', 'middleware', 'config'].map(f => (
71 <button key={f} onClick={() => setFilter(f)} style={{
72 padding: '6px 14px', borderRadius: '6px', border: 'none', cursor: 'pointer',
73 background: filter === f ? '#64ffda' : '#1a1a2e', color: filter === f ? '#0f0f23' : '#8892b0',
74 }}>{f}</button>
75 ))}
76 </div>
77
78 <div style={{ display: 'grid', gridTemplateColumns: '260px 1fr', gap: '20px' }}>
79 <div style={{ display: 'grid', gap: '8px', alignContent: 'start' }}>
80 {filtered.map(comp => (
81 <button key={comp.name} onClick={() => setSelected(comp)} style={{
82 padding: '12px', borderRadius: '8px', textAlign: 'left',
83 border: selected.name === comp.name ? '1px solid #64ffda' : '1px solid #333',
84 background: selected.name === comp.name ? '#1a2a3e' : '#1a1a2e', color: '#e0e0e0', cursor: 'pointer',
85 }}>
86 <code style={{ color: '#64ffda', fontSize: '14px' }}>{comp.name}</code>
87 <div><span style={{ fontSize: '11px', padding: '2px 6px', borderRadius: '4px', background: catColors[comp.category] + '30', color: catColors[comp.category] }}>{comp.category}</span></div>
88 </button>
89 ))}
90 </div>
91 <div style={{ background: '#1a1a2e', padding: '20px', borderRadius: '12px', border: '1px solid #333' }}>
92 <h3 style={{ color: '#64ffda', margin: '0 0 8px' }}>{selected.name}</h3>
93 <span style={{ fontSize: '12px', color: catColors[selected.category] }}>{selected.category}</span>
94 <p style={{ color: '#8892b0', fontSize: '14px' }}>{selected.description}</p>
95 <pre style={{ background: '#0f0f23', padding: '16px', borderRadius: '8px', fontSize: '13px', color: '#e0e0e0', overflow: 'auto', lineHeight: 1.5, whiteSpace: 'pre-wrap' }}>{selected.code}</pre>
96 </div>
97 </div>
98 </div>
99 );
100}
101
102export default ClerkDemo;Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Que fournit Clerk en tant que solution d'authentification dans Next.js ?
2. Quelle est la différence fondamentale entre authentification et autorisation ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Tri vertical
Place le cycle de vie de la gestion des sessions :
- Clique dans l'ordre
Place le chemin de l'URL de callback dans NextAuth :
- Tri horizontal
Place la syntaxe pour récupérer une session serveur dans NextAuth :
- Tri vertical
Place le processus de hachage des mots de passe :
- Tri horizontal
Place la syntaxe de vérification du jeton JWT :
- Tri vertical
Place les éléments dans le bon ordre : Header (type et algorithme) → Payload (claim
- Tri horizontal
Place la syntaxe de vérification de session dans le middleware :
- Tri vertical
Place les éléments dans le bon ordre : Autorisation de l'utilisateur chez le fournisseur → Autori
- Tri horizontal
Place la syntaxe de comparaison de mot de passe bcrypt :
- Clique dans l'ordre
Place les étapes de vérification du jeton JWT :
- Éditeur de code
Crée un système de journalisation des événements d'authentification : connexions, déconnexions, tentatives échouées, événements de sécurité
- Clique dans l'ordre
Place les étapes du flux Authorization Code d'OAuth :