Cours Next.js · Module 7: Authentification et autorisation

Implémenter Clerk dans Next.js

12 min de lecture
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 :

  1. 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
  2. Support natif pour Next.js App Router - optimisé pour fonctionner avec les dernières versions de Next.js
  3. Support multiplateforme - prise en charge de nombreux appareils et plateformes
  4. Authentification multifacteur - prise en charge intégrée de la 2FA
  5. Gestion des organisations et des équipes - des fonctionnalités pour les applications B2B
  6. 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/nextjs

Configuration 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 :

  1. Le coût - contrairement à Auth.js, Clerk fonctionne sur un modèle freemium et exige un abonnement payant pour les applications plus importantes
  2. La dépendance à un service externe - les données d'authentification sont stockées sur les serveurs de Clerk
  3. Moins de contrôle - tu as moins de contrôle sur les détails de l'implémentation de l'authentification
  4. 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 :

  1. 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
  2. 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. 1. Que fournit Clerk en tant que solution d'authentification dans Next.js ?

  2. 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 :

Articles utiles