Kurs Next.js · Moduł 7: Uwierzytelnianie i autoryzacja
Implementacja Clerk w Next.js
W tej lekcji11
W nowoczesnych aplikacjach, niezawodna i bezpieczna obsługa uwierzytelniania oraz zarządzania użytkownikami stanowi fundament dobrego doświadczenia użytkownika. Clerk to kompleksowe rozwiązanie, które upraszcza ten aspekt tworzenia aplikacji, oferując gotowe komponenty i API do zarządzania tożsamością użytkowników w aplikacjach Next.js.
Czym jest Clerk?
Clerk to kompleksowa platforma zarządzania tożsamością (Identity-as-a-Service), zaprojektowana specjalnie pod kątem aplikacji Next.js i React. W przeciwieństwie do innych rozwiązań, Clerk oferuje pełny pakiet narzędzi do uwierzytelniania, zarządzania użytkownikami i zarządzania sesjami.
Główne zalety Clerk:
- Gotowe komponenty UI - pre-stylowane komponenty do logowania, rejestracji, weryfikacji dwustopniowej i więcej
- Natywne wsparcie dla Next.js App Router - zoptymalizowane do pracy z najnowszymi wersjami Next.js
- Wieloplatformowa obsługa - wsparcie dla wielu urządzeń i platform
- Wieloczynnikowe uwierzytelnianie - wbudowana obsługa 2FA
- Zarządzanie organizacjami i teamami - funkcje dla aplikacji B2B
- Zgodność z GDPR i innymi regulacjami - wbudowane mechanizmy ochrony prywatności
Instalacja i konfiguracja
Rozpocznijmy od instalacji pakietów Clerk w projekcie Next.js:
1npm install @clerk/nextjs
2# lub
3yarn add @clerk/nextjs
4# lub
5pnpm add @clerk/nextjsKonfiguracja kluczy API
Po utworzeniu konta na clerk.com i nowej aplikacji, otrzymasz klucze API. Dodaj je do zmiennych środowiskowych w pliku .env.local:
1NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
2CLERK_SECRET_KEY=sk_test_...Jeśli korzystasz z GitHub lub innego systemu kontroli wersji, upewnij się, że plik .env.local jest dodany do .gitignore.
Owinięcie aplikacji w provider Clerk
Aby Clerk działał w całej aplikacji, musimy dodać ClerkProvider w głównym layoucie:
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}Konfiguracja ścieżek publicznych i chronionych
Ważnym krokiem jest określenie, które ścieżki są publiczne (dostępne dla niezalogowanych użytkowników), a które chronione (wymagające zalogowania). Można to zrobić za pomocą 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};W powyższym przykładzie strony główna, kontaktowa i "o nas" są publiczne, a wszystkie inne wymagają zalogowania.
Komponenty uwierzytelniania
Clerk dostarcza gotowe komponenty do najczęstszych operacji związanych z uwierzytelnianiem.
SignIn i SignUp
Zaimplementowanie stron logowania i rejestracji jest bardzo proste:
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}Zwróć uwagę na wzorzec [[...sign-in]] w nazwie folderu - podwójne nawiasy kwadratowe oznaczają opcjonalny parametr catch-all, co pozwala Clerk obsługiwać różne stany uwierzytelniania.
UserButton - przycisk profilu użytkownika
Dodaj przycisk profilu użytkownika do nagłówka, który wyświetla menu z opcjami zarządzania kontem:
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">Moja aplikacja</div>
8 <UserButton afterSignOutUrl="/" />
9 </header>
10 );
11}Dostęp do informacji o użytkowniku
W komponentach po stronie klienta możemy użyć hooków Clerk, aby uzyskać informacje o zalogowanym użytkowniku:
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>Ładowanie...</div>;
11 }
12
13 return (
14 <div className="p-4">
15 <h1 className="text-2xl font-bold mb-4">Twój profil</h1>
16 <div className="mb-4">
17 <img
18 src={user?.imageUrl}
19 alt="Zdjęcie profilowe"
20 className="w-16 h-16 rounded-full"
21 />
22 </div>
23 <p><strong>Imię:</strong> {user?.firstName}</p>
24 <p><strong>Nazwisko:</strong> {user?.lastName}</p>
25 <p><strong>Email:</strong> {user?.primaryEmailAddress?.emailAddress}</p>
26 </div>
27 );
28}W komponentach po stronie serwera, możemy użyć funkcji pomocniczych:
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>Nie jesteś zalogowany</div>;
9 }
10
11 return (
12 <div className="p-4">
13 <h1 className="text-2xl font-bold mb-4">Panel użytkownika</h1>
14 <p>Witaj, {user.firstName}! Oto Twój panel użytkownika.</p>
15 </div>
16 );
17}Zarządzanie organizacjami
Jedną z mocnych stron Clerk jest wbudowana obsługa organizacji (teamów), co jest szczególnie przydatne w aplikacjach B2B.
Komponent selekcji organizacji
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}Tworzenie nowej organizacji
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}Dostęp do danych organizacji
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 // Pobierz aktywną organizację
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">Nie wybrano organizacji</h1>
21 <p>Wybierz organizację, aby zobaczyć jej dashboard.</p>
22 </div>
23 );
24 }
25
26 return (
27 <div className="p-4">
28 <h1 className="text-2xl font-bold mb-4">Dashboard organizacji: {org.name}</h1>
29 <p>Członkowie: {org.membersCount}</p>
30 <p>Utworzono: {new Date(org.createdAt).toLocaleDateString()}</p>
31 </div>
32 );
33}Zabezpieczanie API Routes
W Next.js App Router możemy łatwo zabezpieczać trasy API używając funkcji pomocniczych 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: "To są chronione dane tylko dla zalogowanych użytkowników"
17 });
18}Dostosowywanie wyglądu
Clerk oferuje rozbudowane opcje dostosowywania wyglądu. Można to zrobić na trzy sposoby:
1. Za pomocą 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. Za pomocą dostawcy wyglądu
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. Za pomocą niestandardowych stylów CSS
Można również utworzyć plik CSS z własnymi stylami:
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}A następnie zaimportować do aplikacji:
1// app/layout.tsx
2import "../styles/clerk.css";Webhook i integracja z bazą danych
W większości aplikacji będziesz chciał synchronizować dane użytkowników Clerk z własną bazą danych. Najlepszym sposobem na to jest użycie webhooków:
Konfiguracja endpointu 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('Brak CLERK_WEBHOOK_SECRET');
12 }
13
14 // Pobierz nagłówki
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: "Brak wymaganych nagłówków" }), {
22 status: 400
23 });
24 }
25
26 // Pobierz treść żądania
27 const payload = await req.json();
28 const body = JSON.stringify(payload);
29
30 // Zweryfikuj 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('Błąd weryfikacji webhooka:', err);
42 return new NextResponse(JSON.stringify({ error: "Nieprawidłowy podpis" }), {
43 status: 400
44 });
45 }
46
47 // Obsługa różnych typów zdarzeń
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 // Tutaj dodaj logikę zapisania użytkownika w bazie danych
56 // Na przykład używając 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 // Tutaj zaktualizuj użytkownika w bazie danych
75 break;
76 }
77
78 case 'user.deleted': {
79 const { id } = event.data;
80
81 // Tutaj usuń użytkownika z bazy danych
82 break;
83 }
84
85 // Obsłuż inne typy zdarzeń, takie jak organization.created, organization.updated, itp.
86 }
87
88 return NextResponse.json({ message: "Webhook odebrany" }, { status: 200 });
89}Zaawansowane funkcje
Wieloczynnikowe uwierzytelnianie (2FA)
Clerk oferuje wbudowane wsparcie dla 2FA. Możesz włączyć tę funkcję w panelu administracyjnym Clerk.
Logowanie bezhasłowe
Możesz skonfigurować logowanie za pomocą linków magic link (bez hasła):
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}Wieloetapowe logowanie
Możesz utworzyć niestandardowy proces logowania z wieloma etapami:
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>Ładowanie...</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 || "Wystąpił błąd");
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 // Przekierowanie po zalogowaniu
52 window.location.href = "/dashboard";
53 }
54 } catch (err: any) {
55 setError(err.errors?.[0]?.message || "Kod nieprawidłowy");
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">Logowanie</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 Kontynuuj
88 </button>
89 </form>
90 ) : (
91 <form onSubmit={handleVerifyCode} className="space-y-4">
92 <div>
93 <label htmlFor="code" className="block mb-1">Kod weryfikacyjny</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 Weryfikuj
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 Wróć
117 </button>
118 </form>
119 )}
120 </div>
121 );
122}Wady i ograniczenia Clerk
Choć Clerk oferuje wiele korzyści, warto być świadomym kilku ograniczeń:
- Koszt - w przeciwieństwie do Auth.js, Clerk działa na modelu freemium i dla większych aplikacji wymaga płatnej subskrypcji
- Zależność od zewnętrznej usługi - dane uwierzytelniania są przechowywane na serwerach Clerk
- Mniejsza kontrola - masz mniejszą kontrolę nad implementacją szczegółów uwierzytelniania
- Kustomizacja - mimo wielu opcji dostosowywania, czasami natywne rozwiązanie może oferować więcej swobody
Kiedy wybrać Clerk zamiast Auth.js?
Przy podejmowaniu decyzji warto rozważyć następujące czynniki:
Wybierz Clerk, gdy:
- Potrzebujesz gotowego, kompletnego rozwiązania uwierzytelniania
- Chcesz szybko wdrożyć funkcje uwierzytelniania z minimalnym wysiłkiem
- Potrzebujesz zarządzania organizacjami i teamami
- Potrzebujesz zaawansowanych funkcji jak 2FA, magic links, etc.
- Nie chcesz zarządzać infrastrukturą uwierzytelniania
Wybierz Auth.js, gdy:
- Potrzebujesz większej kontroli nad procesem uwierzytelniania
- Preferujesz rozwiązanie open-source
- Nie chcesz polegać na zewnętrznym serwisie
- Masz ograniczony budżet dla większego projektu
- Musisz przechowywać dane uwierzytelniania we własnej bazie danych
Podsumowanie
Clerk to potężne narzędzie, które może znacząco przyspieszyć implementację uwierzytelniania w aplikacjach Next.js. Oferuje bogaty zestaw funkcji, które mogłyby wymagać tygodni pracy do samodzielnej implementacji. Dla wielu zespołów deweloperskich oszczędność czasu i gotowe rozwiązania bezpieczeństwa przeważają nad kosztami i mniejszą kontrolą.
Jednak wybór między Clerk, Auth.js, czy własnym rozwiązaniem uwierzytelniania powinien być oparty na konkretnych wymaganiach projektu, dostępnym budżecie i potrzebach biznesowych.
Kod do tej lekcji: App.tsx
1import React, { useState } from 'react';
2
3// Demo: Clerk w 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: 'Gotowy komponent logowania z OAuth, email i haslem',
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: 'Komponent rejestracji z weryfikacja 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: 'Awatar z menu (profil, wylogowanie)',
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 do danych zalogowanego uzytkownika',
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 do stanu auth i tokenow',
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 do ochrony tras na 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 opakowujacy aplikacje',
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: 'Instalacja', cmd: 'npm install @clerk/nextjs' },
42 { title: 'Env vars', cmd: 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...\nCLERK_SECRET_KEY=sk_...' },
43 { title: 'Provider', cmd: 'Opakuj <html> w <ClerkProvider>' },
44 { title: 'Middleware', cmd: 'Utworz middleware.ts z clerkMiddleware()' },
45 { title: 'Gotowe!', cmd: 'Uzyj <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 w Next.js</h1>
53 <p style={{ color: '#8892b0', marginBottom: '24px' }}>Gotowe komponenty i hooki do uwierzytelniania</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;Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Co zapewnia Clerk jako rozwiązanie do autentykacji w Next.js?
2. Jaka jest fundamentalna różnica między uwierzytelnianiem (authentication) a autoryzacją (authorization)?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Układanie w pionie
Uporządkuj cykl zarządzania sesją:
- Klikanie w kolejności
Ułóż ścieżkę callback URL w NextAuth:
- Układanie w poziomie
Ułóż składnię pobierania sesji serwerowej w NextAuth:
- Układanie w pionie
Uporządkuj proces hashowania hasła:
- Układanie w poziomie
Ułóż składnię weryfikacji tokena JWT:
- Układanie w pionie
Ułóż elementy w prawidłowej kolejności: Header (typ i algorytm) → Payload (claim
- Układanie w poziomie
Ułóż składnię sprawdzania sesji w middleware:
- Układanie w pionie
Ułóż elementy w prawidłowej kolejności: User authorization na provider → Authori
- Układanie w poziomie
Ułóż składnię porównywania hasła z bcrypt:
- Klikanie w kolejności
Ułóż etapy weryfikacji JWT tokena:
- Edytor kodu
Utwórz system logowania authentication events: logins, logouts, failed attempts, security events
- Klikanie w kolejności
Ułóż etapy OAuth authorization code flow: