WisarWisar
Dasturlash kitobi/13-QISM — NextJS56 daqiqa

13.9-bob: Autentifikatsiya (NextAuth / Auth.js)

13-QISM — Next.js · 9-mavzu


1. Kirish va motivatsiya

Deyarli har real ilova bitta savolga javob berishi kerak: "Bu foydalanuvchi kim?" Onlayn do'kon (kim xarid qiladi, savatda nima), ijtimoiy tarmoq (kimning profili), bank ilovasi (kimning puli), admin panel (kim boshqaradi) — bularning hammasi autentifikatsiya (foydalanuvchini tanish — login/ro'yxat) va avtorizatsiya (u nima qila oladi — ruxsatlar)ga muhtoj. Bu — web ilovaning eng nozik, eng xavfsizlik talab qiladigan qismi (xato qilsangiz — foydalanuvchi ma'lumotlari o'g'irlanadi, hisoblar buziladi). Shuning uchun o'zingiz noldan yozish o'rniga (xavfli — ko'p nozik joy bor) sinab ko'rilgan kutubxona ishlatish maqsadga muvofiq: Next.js'da bu Auth.js (eski nomi NextAuth.js).

Auth.js — Next.js uchun eng mashhur, ishonchli autentifikatsiya yechimi. U murakkab narsani (sessiya boshqaruvi, OAuth oqimi, CSRF himoya, token) sizning o'rningizga qiladi, sizga esa sodda API beradi. U ko'p login usulini qo'llab-quvvatlaydi: ijtimoiy login (Google, GitHub bilan — "Google bilan kirish" tugmasi — foydalanuvchi parol eslab qolishi shart emas), email/parol (an'anaviy), va "sehrli havola" (email'ga havola — parolsiz). Va u Next.js bilan chuqur integratsiyalangan: server'da sessiyani olish (auth()), client'da (useSession), middleware bilan himoya 13.6-bob, Server Actions himoyasi 13.5-bob.

Bu bob: autentifikatsiya asoslari (authn vs authz), Auth.js sozlash, provayderlar (OAuth — Google/GitHub, Credentials — email/parol), sessiya strategiyasi (JWT vs database), sessiyani olish (server auth(), client useSession), sahifani himoya (middleware + server — 13.6), Server Action/Route himoyasi, rollar (admin/foydalanuvchi), callbacklar (jwt, session), kirish/chiqish, va xavfsizlik (CSRF, parol — bcrypt). Har mavzu to'liq, har kod misolida maqsad + izoh + "nima qiladi" bilan ochib beriladi.

O'xshatish: Autentifikatsiya va avtorizatsiya — bu klubga kirish va VIP zona. Autentifikatsiya (authn — "kimsan?") — bu eshik oldidagi qorovul: u sizning kimligingizni tekshiradi (pasport — parol, yoki taklif kartasi — Google login). Tasdiqlansa — klubga kirasiz (sessiya — qo'lingizga bilaguzuk taqishadi, endi har xonada qayta pasport ko'rsatish shart emas). Avtorizatsiya (authz — "nima qila olasiz?") — bu VIP zona qorovuli: siz klubga kirdingiz (autentifikatsiya), lekin VIP zonaga faqat VIP bilaguzukli odamlar kira oladi (rol — admin). Oddiy mehmon (foydalanuvchi) raqs maydonida (oddiy sahifalar), VIP (admin) yuqori qavatda (admin panel). Auth.js — bu professional xavfsizlik kompaniyasi (siz o'zingiz qorovul yollab, bilaguzuk tizimini o'ylab topish o'rniga — tayyor, sinagan tizimni ishlatasiz). O'zingiz qilganda bir teshik qoldirsa — butun klub xavf ostida (ma'lumot o'g'irlanadi); professional kompaniya bularni hisobga olgan.

Nega muhim?

  • Har real ilovada kerak — foydalanuvchi tizimi (login, profil, shaxsiy ma'lumot) deyarli har loyihada.
  • Xavfsizlik kritik — autentifikatsiya xatosi = ma'lumot o'g'irlanishi (o'zingiz yozish xavfli — kutubxona xavfsizroq).
  • Auth.js soddalashtiradi — OAuth, sessiya, CSRF — murakkab narsani tayyor beradi.
  • Next.js integratsiya — server/client/middleware/Server Actions — hammasi bilan ishlaydi.

2. Nazariya — chuqur tushuntirish

2.1. Autentifikatsiya vs avtorizatsiya

text
  IKKI TUSHUNCHA (ko'p chalkashtiriladi — aniq farq):

  AUTENTIFIKATSIYA (Authentication — "KIMSAN?"):
   foydalanuvchini TANISH (login, ro'yxat — kim ekanini tasdiqlash)
   parol, Google login, token — "bu haqiqatan Ali"

  AVTORIZATSIYA (Authorization — "NIMA QILA OLASAN?"):
   ruxsatlar (autentifikatsiyadan KEYIN — kim ekanini bilgach)
   "Ali admin  admin panelga kira oladi"; "Vali oddiy  faqat o'z profili"

  OQIM:
  1. Autentifikatsiya: kim? (login  Ali)
  2. Avtorizatsiya: Ali nima qila oladi? (rol  admin  ruxsat)

  ┌────────────────────────────────────────────────────────────┐
  │ Authn: KIMSAN (login) | Authz: NIMA QILA OLASAN (ruxsat)    │
  │  avval authn (tanish), keyin authz (ruxsat tekshir)        │
  └────────────────────────────────────────────────────────────┘

   Authn (kimsan — login) ≠ Authz (nima qila olasan — rol/ruxsat)
   Avval autentifikatsiya (tanish), keyin avtorizatsiya (ruxsat) — ikkalasi ham kerak

Autentifikatsiya vs avtorizatsiya — ikki asosiy, lekin ko'p chalkashtiriladigan tushuncha. Autentifikatsiya (Authentication — qisqa "authn") — "kimsan?" — foydalanuvchini tanish (login/ro'yxat orqali kim ekanini tasdiqlash): parol, Google login, token bilan "bu haqiqatan Ali" ekanini aniqlash. Avtorizatsiya (Authorization — "authz") — "nima qila olasiz?" — ruxsatlar (autentifikatsiyadan keyin — kim ekanini bilgach): "Ali admin admin panelga kira oladi", "Vali oddiy foydalanuvchi faqat o'z profilini tahrir qila oladi". Oqim: (1) avval autentifikatsiya (kim? — login "Ali"); (2) keyin avtorizatsiya (Ali nima qila oladi? — rol admin ruxsat tekshir). Ya'ni avval tanish (authn), keyin ruxsat (authz). Misol: siz bankda — autentifikatsiya (pasport — kim ekaningiz), avtorizatsiya (faqat o'z hisobingizga kirish — boshqa odamnikiga emas). Ikki nuqta: (1) authn (kimsan — login) ≠ authz (nima qila olasiz — rol/ruxsat) — bular boshqa-boshqa narsa (chalkashtirmaslik kerak); (2) avval autentifikatsiya (tanish), keyin avtorizatsiya (ruxsat) — ikkalasi ham kerak (faqat login yetarli emas — kim nima qila olishini ham tekshirish kerak). Bu farqni tushunish — xavfsiz ilovaning asosi (13.5: 2.11, 13.6: Misol 10'da Server Action/Route'da avtorizatsiya tekshirdik — endi to'liq tizim). Auth.js asosan autentifikatsiyani (login) qiladi, avtorizatsiya (rol/ruxsat)ni esa siz qo'shasiz (callbacklar + tekshiruv — 2.9). Bu — har xavfsizlik suhbatining boshlanishi.

2.2. Auth.js (NextAuth) — nima va sozlash

text
  AUTH.JS (NextAuth v5) — Next.js uchun autentifikatsiya kutubxonasi:

  // auth.ts (loyiha ildizida — markaziy sozlama)
  import NextAuth from "next-auth";
  import GitHub from "next-auth/providers/github";

  export const { handlers, auth, signIn, signOut } = NextAuth({
    providers: [GitHub],   // login usullari 2.3-bob
    // sozlamalar (session, callbacks — 2.6, 2.9)
  });

  // app/api/auth/[...nextauth]/route.ts — Auth.js endpoint (OAuth oqimi):
  import { handlers } from "@/auth";
  export const { GET, POST } = handlers;   // Auth.js o'z route'ini boshqaradi

  AUTH.JS NIMA BERADI:
   handlers — auth API endpoint (OAuth oqimi, callback)
   auth() — sessiyani olish (server'da — 2.5)
   signIn/signOut — kirish/chiqish funksiyalari 2.8-bob
   Sessiya, CSRF, token — avtomatik (xavfsizlik)

   Auth.js — markaziy auth.ts (NextAuth() sozlama); handlers route'da; auth/signIn/signOut export
   Murakkab narsani (OAuth, sessiya, CSRF) avtomatik qiladi — siz sozlamani berasiz

Auth.js (NextAuth) — nima va sozlash — Next.js'ning standart autentifikatsiya yechimi. Auth.js (v5 — eski nomi NextAuth.js) — Next.js uchun eng mashhur autentifikatsiya kutubxonasi. Sozlash: auth.ts faylda (loyiha ildizida — markaziy) NextAuth({...}) chaqiriladi va u to'rt narsani qaytaradi: handlers (auth API endpoint), auth (sessiyani olish), signIn/signOut (kirish/chiqish). providers — login usullari (GitHub, Google, email/parol — 2.3). Keyin app/api/auth/[...nextauth]/route.tsda handlersni export qilinadi (export const { GET, POST } = handlers) — bu Auth.js'ga o'z route'ini (OAuth oqimi, callback, sessiya) boshqarishga imkon beradi ([...nextauth] — catch-all — 13.2: 2.2 — Auth.js'ning barcha auth yo'llari). Auth.js nima beradi: (1) handlers — auth API endpoint (OAuth oqimi, callback — avtomatik); (2) auth() — sessiyani olish (server'da — 2.5); (3) signIn/signOut — kirish/chiqish funksiyalari 2.8-bob; (4) sessiya, CSRF himoya, token — avtomatik (xavfsizlik — siz o'ylamaysiz). Ikki nuqta: (1) Auth.js — markaziy auth.ts (NextAuth() sozlama), handlers route'da, auth/signIn/signOut export (markaziy konfiguratsiya); (2) murakkab narsani (OAuth oqimi, sessiya boshqaruvi, CSRF himoya, token) avtomatik qiladi — siz faqat sozlamani (provayderlar, callbacklar) berasiz. Bu — autentifikatsiyani o'zingiz noldan yozishdan (xavfli — ko'p nozik joy) qutqaradi (sinagan, xavfsiz kutubxona). Auth.js — Next.js auth'ning "de-fakto standarti".

2.3. Provayderlar (OAuth, Credentials)

text
  PROVAYDERLAR — login USULLARI (Auth.js ko'p usulni qo'llab-quvvatlaydi):

  // auth.ts
  import GitHub from "next-auth/providers/github";
  import Google from "next-auth/providers/google";
  import Credentials from "next-auth/providers/credentials";

  providers: [
    // 1. OAUTH (ijtimoiy login — "Google bilan kirish" — parolsiz, oson):
    Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }),
    GitHub,

    // 2. CREDENTIALS (email/parol — an'anaviy):
    Credentials({
      credentials: { email: {}, password: {} },
      authorize: async (creds) => {
        const user = await db.user.findUnique({ where: { email: creds.email } });
        if (user && await bcrypt.compare(creds.password, user.passwordHash)) {
          return user;   //  to'g'ri  user; noto'g'ri  null
        }
        return null;
      },
    }),
  ],

   OAuth (Google/GitHub) — ijtimoiy login (parolsiz, oson, xavfsiz); Credentials — email/parol
   Credentials authorize — parolni bcrypt.compare bilan tekshir (HECH QACHON ochiq parol — 2.10)

Provayderlar (OAuth, Credentials) — login usullari. Provayder — login usuli; Auth.js ko'p turni qo'llab-quvvatlaydi. Ikki asosij tur: (1) OAuth (ijtimoiy login — Google, GitHub, Facebook): Google({ clientId, clientSecret }) — "Google bilan kirish" tugmasi (foydalanuvchi Google hisobi bilan kiradi — parol eslab qolishi shart emas, Auth.js OAuth oqimini boshqaradi — xavfsiz, oson; clientId/clientSecret Google Console'dan olinadi — secret server'da — env). Bu eng oson va xavfsiz usul (parol sizning serveringizda saqlanmaydi — Google boshqaradi); (2) Credentials (email/parol — an'anaviy): Credentials({ authorize })authorize funksiya foydalanuvchi kiritgan email/parolni tekshiradi: db.user.findUnique (email bo'yicha topish) + bcrypt.compare(parol, user.passwordHash) (parol hash bilan mos keladimi — 2.10), to'g'ri bo'lsa user qaytaradi (kirdi), noto'g'ri bo'lsa null (rad). Ikki nuqta: (1) OAuth (Google/GitHub) — ijtimoiy login (parolsiz, oson, xavfsiz — parol sizda saqlanmaydi), Credentials — email/parol (an'anaviy, lekin parolni siz saqlaysiz — mas'uliyat); (2) Credentials authorizeda parolni bcrypt.compare bilan tekshir (hech qachon ochiq parolni solishtirmang — parol hash qilingan holda saqlanadi — 2.10). Ko'p sayt ikkalasini beradi (Google bilan tez, yoki email/parol). OAuth — foydalanuvchi uchun oson (parol eslab qolish yo'q), Credentials — to'liq nazorat (lekin xavfsizlik mas'uliyati sizda — parol hash, validatsiya). Yangi loyihada OAuth (Google) bilan boshlash tavsiya (oson, xavfsiz); Credentials kerak bo'lsa — bcrypt + ehtiyot. Provayderlar — Auth.js'ning moslashuvchanlik kuchi (ko'p login usuli, bir tizim).

2.4. Sessiya strategiyasi (JWT vs database)

text
  SESSIYA — foydalanuvchi kirgach, "kirgan" holatini SAQLASH (ikki strategiya):

  1. JWT (JSON Web Token — default — token cookie'da):
   foydalanuvchi ma'lumoti COOKIE'dagi imzolangan token'da (server saqlamaydi)
   har so'rovda token tekshiriladi (DB so'rovsiz — tez, kengaytiriladigan)
   kamchilik: token'ni darrov bekor qilish qiyin (logout — token muddati tugaguncha)

  2. DATABASE SESSION (sessiya DB'da saqlanadi):
   sessiya ID cookie'da, ma'lumot DB'da (har so'rovda DB tekshiriladi)
   afzallik: sessiyani darrov bekor qilish (DB'dan o'chir — logout darrov)
   kamchilik: har so'rovda DB so'rov (sekinroq, lekin nazoratli)

  // auth.ts
  session: { strategy: "jwt" }       // yoki "database" (adapter bilan — 2.7)

  ┌────────────────────────────────────────────────────────────┐
  │ JWT: token cookie'da (tez, DB'siz) | Database: DB'da (nazoratli)│
  │ JWT — default, kengaytiriladigan; Database — darrov bekor qilish│
  └────────────────────────────────────────────────────────────┘

   JWT — token cookie'da (tez, DB so'rovsiz, lekin bekor qilish qiyin); Database — DB'da (nazoratli)
   Ko'p loyiha JWT (default — tez); bank/nozik — database (darrov bekor qilish kerak)

Sessiya strategiyasi (JWT vs database) — kirgan holatni saqlashning ikki usuli. Foydalanuvchi kirgach (autentifikatsiya), "kirgan" holatini saqlash kerak (har sahifada qayta login so'ramaclik uchun — sessiya). Ikki strategiya: (1) JWT (JSON Web Token — Auth.js default) — foydalanuvchi ma'lumoti cookie'dagi imzolangan token'da saqlanadi (server hech narsa saqlamaydi — "stateless"); har so'rovda token tekshiriladi (imzo to'g'rimi — DB so'rovsiz — tez, ko'p server'da ishlaydi — kengaytiriladigan); kamchilik — token'ni darrov bekor qilish qiyin (logout qilsangiz ham token o'z muddati tugaguncha "haqiqiy" — chunki server saqlamaydi, tekshira olmaydi); (2) Database session — sessiya DB'da saqlanadi (sessiya ID cookie'da, to'liq ma'lumot DB'da); har so'rovda DB tekshiriladi; afzallik — sessiyani darrov bekor qilish (DB'dan o'chirsangiz — logout darrov ishlaydi — masalan "barcha qurilmalardan chiqish"); kamchilik — har so'rovda DB so'rov (sekinroq, lekin nazoratli). Sozlash: session: { strategy: "jwt" } yoki "database" (adapter bilan — 2.7). Ikki nuqta: (1) JWT — token cookie'da (tez, DB so'rovsiz, kengaytiriladigan, lekin darrov bekor qilish qiyin); Database — DB'da (nazoratli, darrov bekor qilish mumkin, lekin har so'rovda DB); (2) ko'p loyiha JWT (default — tez, sodda); bank/nozik ilova database (sessiyani darrov bekor qilish kerak — xavfsizlik). Tanlov — loyiha ehtiyojiga bog'liq (tezlik vs nazorat). Ko'p holda JWT yetarli (default — Auth.js uni yaxshi boshqaradi). Sessiya — autentifikatsiya bilan login orasidagi "ko'prik" (bir marta kirib, sessiya davomida kirgan qolish).

2.5. Sessiyani olish (server va client)

text
  SESSIYANI OLISH — foydalanuvchi kim (server'da va client'da — har xil):

  // SERVER (Server Component, Server Action, Route Handler — auth()):
  import { auth } from "@/auth";
  export default async function Page() {
    const session = await auth();          //  server'da sessiya
    if (!session) return <p>Kiring</p>;
    return <p>Salom, {session.user.name}!</p>;
  }

  // CLIENT (Client Component — useSession — SessionProvider kerak):
  "use client";
  import { useSession } from "next-auth/react";
  export default function Profile() {
    const { data: session, status } = useSession();   // client'da sessiya
    if (status === "loading") return <Spinner />;
    if (!session) return <p>Kiring</p>;
    return <p>Salom, {session.user.name}!</p>;
  }

  // SessionProvider (client uchun — layout'da):
  // <SessionProvider><App /></SessionProvider>

   Server: auth() (Server Component/Action/Route — to'g'ridan, tez); Client: useSession (hook)
   useSession — SessionProvider kerak (layout'da o'rab); server'da auth() afzal (tezroq, xavfsizroq)

Sessiyani olish (server va client) — foydalanuvchi kimligini aniqlash. Sessiyani ikki joyda olish mumkin (har xil usul): (1) server'da (Server Component, Server Action, Route Handler) — auth() (Auth.js'dan): const session = await auth() — server'da sessiyani to'g'ridan oladi (session.user — foydalanuvchi ma'lumoti; session null bo'lsa — kirmagan). Bu afzal usul (server'da — tezroq, xavfsizroq, qo'shimcha so'rov yo'q); (2) client'da (Client Component) — useSession (next-auth/react'dan): const { data: session, status } = useSession() — client'da sessiyani hook orqali oladi (status"loading"/"authenticated"/"unauthenticated"). useSession ishlashi uchun SessionProvider kerak (layout'da butun ilovani o'rab — Context orqali sessiyani tarqatadi — 12.1). Ikki nuqta: (1) server'da auth() (Server Component/Action/Route — to'g'ridan, tez, xavfsiz); client'da useSession (hook — interaktiv komponentda); (2) useSessionSessionProvider kerak (layout'da o'rab); server'da auth() afzal (tezroq — server render, xavfsizroq — token brauzerga chiqmaydi). Qachon qaysi: ko'p holda server'da auth() (Server Component'da foydalanuvchini tekshirish — 13.3), client'da useSession faqat interaktiv komponentda kerak bo'lsa (masalan navbar'da "Kirish/Chiqish" tugmasi — foydalanuvchi holatiga qarab). Server-first 13.3-bob — auth'da ham (server'da tekshirish afzal — tezroq, xavfsizroq). Bu — sessiyani har joyda (server, client) olishning usuli.

2.6. Callbacklar (jwt, session)

text
  CALLBACKLAR — token/sessiyaga qo'shimcha ma'lumot qo'shish (rol, ID — sozlash):

  // auth.ts — callbacks (token va sessiyani moslash):
  callbacks: {
    // jwt — token yaratilganda/yangilanganda (rol qo'shish):
    async jwt({ token, user }) {
      if (user) {                       // birinchi marta (login paytida)
        token.role = user.role;         //  rolni token'ga qo'sh (DB'dan)
        token.id = user.id;
      }
      return token;
    },
    // session — sessiya o'qilganda (token'dan sessiyaga):
    async session({ session, token }) {
      session.user.role = token.role;   //  rolni sessiyaga (komponentda ishlatish uchun)
      session.user.id = token.id;
      return session;
    },
  },

   endi session.user.role bor (avtorizatsiya uchun — 2.9):
  // const session = await auth();
  // if (session.user.role === "admin") { ... }

   jwt callback — token'ga ma'lumot (rol, id — DB'dan, login paytida); session — token'dan sessiyaga
   Callbacklar — sessiyaga rol/ID qo'shish (avtorizatsiya uchun — default'da faqat name/email)

Callbacklar (jwt, session) — token va sessiyani moslashtirish (rol, ID qo'shish). Default'da Auth.js sessiyaga faqat asosiy ma'lumotni (name, email, image) qo'yadi. Lekin avtorizatsiya 2.9-bob uchun rol (admin/foydalanuvchi) va ID kerak — bularni callbacklar orqali qo'shasiz. Ikki asosiy callback: (1) jwt — token yaratilganda/yangilanganda ishlaydi: async jwt({ token, user }) { if (user) { token.role = user.role; token.id = user.id } return token } — birinchi marta (login paytida — user mavjud — DB'dagi foydalanuvchi), rolni va ID'ni token'ga qo'shadi (keyingi so'rovlarda token'dan o'qiladi — DB'siz); (2) session — sessiya o'qilganda ishlaydi: async session({ session, token }) { session.user.role = token.role; session.user.id = token.id; return session } — token'dagi rolni/ID'ni sessiyaga ko'chiradi (endi komponentda session.user.role mavjud). Natija: const session = await auth(); if (session.user.role === "admin") {...} — avtorizatsiya uchun rol mavjud. Ikki nuqta: (1) jwt callback — token'ga ma'lumot (rol, id — DB'dan, login paytida — bir marta), session callback — token'dan sessiyaga (har o'qishda); (2) callbacklar — sessiyaga rol/ID qo'shish (avtorizatsiya uchun — default faqat name/email beradi). Bu — autentifikatsiya (Auth.js — kim) va avtorizatsiya (rol — nima qila oladi — 2.1)ni bog'laydi (rol sessiyaga qo'shilgach, har joyda ruxsat tekshirsa bo'ladi). Callbacklar — Auth.js'ni o'z ilovangizga moslashtirishning asosiy vositasi (rol, qo'shimcha ma'lumot, maxsus mantiq). Rol-asosli avtorizatsiya uchun callbacklar majburiy (rolni sessiyaga olib chiqish).

2.7. Database adapter (Prisma)

text
  ADAPTER — Auth.js'ni DB bilan bog'lash (foydalanuvchi, sessiya DB'da saqlanadi):

  // auth.ts — Prisma adapter (foydalanuvchi/sessiyani DB'da saqla):
  import { PrismaAdapter } from "@auth/prisma-adapter";
  import { db } from "@/lib/db";

  export const { handlers, auth } = NextAuth({
    adapter: PrismaAdapter(db),   //  Auth.js DB'ni boshqaradi (User, Session, Account jadvallari)
    providers: [Google],
    session: { strategy: "database" },   // adapter bilan database session 2.4-bob
  });

  ADAPTER NIMA QILADI:
   Foydalanuvchi yaratadi (yangi login  DB'da User)
   OAuth hisoblarini bog'laydi (Account — Google ID  User)
   Sessiyani DB'da (database strategiya — 2.4)
   Auth.js standart sxema (User, Account, Session, VerificationToken)

   Adapter — Auth.js'ni DB'ga ulaydi (foydalanuvchi/OAuth hisob/sessiya DB'da — doimiy)
   Prisma adapter 6.12-bob — standart sxema; OAuth + database session uchun kerak

Database adapter (Prisma) — Auth.js'ni ma'lumotlar bazasiga bog'lash. Adapter — Auth.js'ni DB bilan bog'laydi (foydalanuvchi, OAuth hisoblari, sessiya DB'da saqlanishi uchun). Prisma adapter 6.12-bob: adapter: PrismaAdapter(db) — Auth.js DB'ni boshqaradi (kerakli jadvallarni — User, Account, Session, VerificationToken — ishlatadi). Adapter nima qiladi: (1) foydalanuvchi yaratadi (yangi odam birinchi marta login qilsa — DB'da User yozyvi yaratiladi — doimiy saqlanadi); (2) OAuth hisoblarini bog'laydi (Account jadvali — masalan Google ID User — bir foydalanuvchi Google va GitHub bilan ham kirsa, bir hisobga bog'lanadi); (3) sessiyani DB'da saqlaydi (database strategiya — 2.4); (4) Auth.js standart sxemasini (User, Account, Session, VerificationToken jadvallari — Auth.js hujjatida berilgan) ishlatadi. Ikki nuqta: (1) adapter — Auth.js'ni DB'ga ulaydi (foydalanuvchi/OAuth hisob/sessiya DB'da — doimiy saqlanadi — server qayta ishga tushca ham qoladi); (2) Prisma adapter (6.12 — yoki Drizzle, MongoDB adapter) — standart sxema; OAuth + database session uchun kerak (JWT-only, Credentials-only holda adapter shart emas — lekin OAuth uchun foydalanuvchíni DB'da saqlash kerak). Qachon kerak: OAuth (Google/GitHub) ishlatsangiz — adapter (foydalanuvchini DB'da saqlash), yoki database session 2.4-bob. Faqat JWT + Credentials bo'lsa — adapter ixtiyoriy (siz o'z User jadvalingizni boshqarasiz). Adapter — Auth.js'ni sizning DB'ngiz bilan integratsiya qiladi (foydalanuvchi ma'lumotlari doimiy — profil, rol, hisob). Prisma 6.12-bob bilan — eng keng kombinatsiya.

2.8. Kirish va chiqish (signIn, signOut)

text
  KIRISH/CHIQISH — login va logout (server va client'da):

  // SERVER (Server Action — forma bilan — progressive enhancement — 13.5):
  // auth.ts'dan signIn/signOut
  import { signIn, signOut } from "@/auth";

  // Kirish formasi (Server Action):
  <form action={async () => { "use server"; await signIn("github"); }}>
    <button>GitHub bilan kirish</button>
  </form>

  // Chiqish:
  <form action={async () => { "use server"; await signOut(); }}>
    <button>Chiqish</button>
  </form>

  // CLIENT (Client Component — next-auth/react):
  "use client";
  import { signIn, signOut } from "next-auth/react";
  <button onClick={() => signIn("google")}>Google bilan kirish</button>
  <button onClick={() => signOut()}>Chiqish</button>

  // Credentials (email/parol):
  await signIn("credentials", { email, password, redirectTo: "/dashboard" });

   signIn(provider) — kirish (OAuth yoki credentials); signOut() — chiqish (server yoki client)
   Server (Server Action — forma) yoki client (onClick); redirectTo — kirgach qaerga

Kirish va chiqish (signIn, signOut) — login va logout amallari. signIn (kirish) va signOut (chiqish) — ikki joyda ishlatsa bo'ladi: (1) server'da (Server Action — forma bilan — progressive enhancement — 13.5): auth.ts'dagi signIn/signOutni ishlatish: <form action={async () => { "use server"; await signIn("github") }}> (GitHub bilan kirish — forma submit Server Action OAuth oqimi), <form action={async () => { "use server"; await signOut() }}> (chiqish); (2) client'da (Client Component): next-auth/react'dagi signIn/signOut: <button onClick={() => signIn("google")}> (tugma hodisasi — 13.5: 2.7). Credentials (email/parol): await signIn("credentials", { email, password, redirectTo: "/dashboard" }) — provayder nomi "credentials", ma'lumot (email/parol), redirectTo (kirgach qaerga — dashboard). signIn("github")/signIn("google") — OAuth (provayder sahifasiga yo'naltiradi — foydalanuvchi Google/GitHub'da tasdiqlaydi, qaytadi). Ikki nuqta: (1) signIn(provider) — kirish (OAuth provayder yoki credentials), signOut() — chiqish (ikkalasi server yoki client'da); (2) server (Server Action — forma — progressive enhancement, JS'siz ham ishlaydi) yoki client (onClick — interaktiv), redirectTo — kirgach qaerga. Server vs client: server (Server Action) — progressive enhancement (forma JS'siz ham — 13.5: 2.5 — ishonchli), client — darrov interaktiv (loading holati bilan). Ko'p holda server (Server Action — forma) afzal (ishonchli, sodda). signIn/signOut — autentifikatsiya oqimining (kirish/chiqish) amaliy nuqtasi. Auth.js bularni soddalashtiradi (OAuth oqimi, sessiya yaratish — avtomatik).

2.9. Avtorizatsiya — rollar va himoya

text
  AVTORIZATSIYA — rol va ruxsat tekshirish (autentifikatsiyadan keyin — 2.1):

  // 1. SAHIFA darajasida (Server Component — rol tekshir):
  export default async function AdminPage() {
    const session = await auth();
    if (!session) redirect("/login");                    // kirmagan  login
    if (session.user.role !== "admin") redirect("/");    //  admin emas  bosh sahifa
    return <AdminPanel />;   // faqat admin ko'radi
  }

  // 2. MIDDLEWARE darajasida (markaziy — 13.6):
  // middleware'da rol tekshir  /admin'ga faqat admin

  // 3. SERVER ACTION/ROUTE darajasida (13.5: 2.11, 13.6: Misol 10):
  "use server";
  export async function deleteUser(id: string) {
    const session = await auth();
    if (session?.user.role !== "admin") throw new Error("Ruxsat yo'q");  //  tekshir
    await db.user.delete({ where: { id } });
  }

   Avtorizatsiya — har himoyalangan joyda rol tekshir (sahifa + Server Action/Route)
   FAQAT sahifada tekshirish YETARLI EMAS — Server Action/API'da HAM (ochiq — 13.5: 2.11)

Avtorizatsiya — rollar va himoya — kim nima qila olishini tekshirish (2.1 — authz). Autentifikatsiyadan keyin (kim — login), avtorizatsiya (rol/ruxsat tekshirish) uch joyda: (1) sahifa darajasida (Server Component): const session = await auth(); if (!session) redirect("/login"); if (session.user.role !== "admin") redirect("/") — kirmagan bo'lsa login'ga, admin emas bo'lsa bosh sahifaga (faqat admin AdminPanel'ni ko'radi — 13.2: Misol 8'ning rol bilan kengaytirilgan versiyasi); (2) middleware darajasida (markaziy — 13.6: Misol 4 — /adminga faqat admin); (3) Server Action/Route darajasida (13.5: 2.11, 13.6: Misol 10): const session = await auth(); if (session?.user.role !== "admin") throw new Error("Ruxsat yo'q") — har mutation/API'da rol tekshir. Eng muhim: faqat sahifada tekshirish yetarli emas — Server Action va Route Handler'da ham tekshirish shart (chunki ular ochiq endpoint — sahifani aylanib o'tib to'g'ridan chaqirsa bo'ladi — 13.5: 2.11, 13.6: 2.6). Masalan: admin sahifani yashirsangiz ham, agar deleteUser Server Action'i rol tekshirmasa — oddiy foydalanuvchi uni to'g'ridan chaqirib, foydalanuvchi o'chira oladi (jiddiy teshik). Ikki nuqta: (1) avtorizatsiya — har himoyalangan joyda rol tekshir (sahifa + Server Action/Route — har qatlam); (2) faqat sahifada tekshirish yetarli emas — Server Action/API'da ham (ochiq — aylanib o'tilishi mumkin — 13.5: 2.11). Bu — chuqur himoya (defense in depth — 14-QISM): har qatlam o'zini himoya qiladi (UI yashirish — UX uchun, lekin haqiqiy himoya har Server Action/API'da). Rol-asosli avtorizatsiya — admin panel, ko'p darajali ruxsat (foydalanuvchi/moderator/admin)ning asosi. Auth.js rolni beradi (callbacklar — 2.6), tekshirish sizning zimmangizda (har joyda).

2.10. Xavfsizlik va best practices

text
  AUTENTIFIKATSIYA XAVFSIZLIGI (eng nozik soha — ehtiyot):

   PAROL HASH — hech qachon ochiq parol saqlama (bcrypt/argon2):
  const hash = await bcrypt.hash(password, 10);   // ro'yxatda (hash saqlanadi)
  await bcrypt.compare(password, hash);            // kirishda (solishtirish)

   SECRET — AUTH_SECRET (token imzosi — kuchli, env'da):
  // .env: AUTH_SECRET=<uzun tasodifiy satr> (npx auth secret bilan generatsiya)

   HTTPS — production'da majburiy (cookie/token shifrlangan kanal)
   CSRF — Auth.js avtomatik (token bilan — siz o'ylamaysiz)
   Avtorizatsiya har qatlamda (sahifa + Server Action/API — 2.9)
   Sessiya muddati (maxAge — token cheksiz emas)

  BEST PRACTICES:
   OAuth (Google/GitHub) afzal (parol siz saqlamaysiz — xavfsizroq)
   Credentials  bcrypt + validatsiya (Zod)
   Rol callbacks orqali 2.6-bob; tekshir har joyda 2.9-bob
   Secret env'da (hech qachon kodda); HTTPS production

   Parol HASH (bcrypt — ochiq emas); AUTH_SECRET kuchli; HTTPS; CSRF avtomatik (Auth.js)
   Best: OAuth afzal, bcrypt, rol tekshir har qatlamda, secret env, sessiya muddati

Xavfsizlik va best practices — autentifikatsiyaning eng nozik tomoni. Xavfsizlik (autentifikatsiya — eng nozik soha — xato qilsangiz ma'lumot o'g'irlanadi): (1) parol hash — hech qachon ochiq (plain) parolni saqlamang — bcrypt (yoki argon2) bilan hash: bcrypt.hash(password, 10) (ro'yxatda — hash saqlanadi, ochiq parol hech qaerda yo'q), bcrypt.compare(password, hash) (kirishda — solishtirish — 2.3). Agar ochiq parol saqlasangiz va DB sizib ketsa — barcha foydalanuvchi paroli ochiq (falokat); hash bilan — parollar himoyalangan; (2) secretAUTH_SECRET (token imzosi uchun — kuchli, tasodifiy — npx auth secret bilan generatsiya, env'da — hech qachon kodda); (3) HTTPS — production'da majburiy (cookie/token shifrlangan kanalda — aks holda o'g'irlanadi); (4) CSRF — Auth.js avtomatik himoya qiladi (token bilan — siz o'ylamaysiz); (5) avtorizatsiya har qatlamda (sahifa + Server Action/API — 2.9); (6) sessiya muddati (maxAge — token cheksiz emas — vaqti-vaqti qayta login). Best practices: OAuth (Google/GitHub) afzal (parol siz saqlamaysiz — Google boshqaradi — xavfsizroq); Credentials bcrypt + validatsiya (Zod — 13.5: 2.9); rol callbacks orqali 2.6-bob, tekshir har joyda 2.9-bob; secret env'da, HTTPS production. Ikki nuqta: (1) parol hash (bcrypt — ochiq emas), AUTH_SECRET kuchli, HTTPS, CSRF avtomatik (Auth.js — bularning ko'rini hal qiladi); (2) best: OAuth afzal, bcrypt, rol tekshir har qatlamda, secret env, sessiya muddati. Bu — 14-QISM (Xavfsizlik)da chuqurroq ochiladi — bu yerda autentifikatsiya xavfsizligining asoslari ko'rsatildi. Auth.js ko'p xavfsizlik ishini (CSRF, token, sessiya) qiladi, lekin parol hash, secret, avtorizatsiya tekshir — sizning zimmangizda. Autentifikatsiya — o'zingiz noldan yozishdan (xavfli) kutubxona (Auth.js) afzal, lekin baribir ehtiyot (xavfsizlik — doimiy mas'uliyat).

text
  OAuth VA CREDENTIALS'DAN TASHQARI PROVAYDERLAR:

  // 1. EMAIL (magic link — parolsiz — email'ga havola yuboriladi):
  import Resend from "next-auth/providers/resend";     // yoki Nodemailer
  providers: [
    Resend({ from: "no-reply@sayt.com" }),             // sozlash: SMTP/API kaliti
  ]
   foydalanuvchi email kiritadi  email'ga "sehrli havola"  bosadi  kirdi
   parol YO'Q; VerificationToken jadvali kerak (adapter — 2.7)

  // 2. WebAuthn / PASSKEY (barmoq izi, Face ID, xavfsizlik kaliti — parolsiz):
  import Passkey from "next-auth/providers/passkey";
  experimental: { enableWebAuthn: true },              // v5 experimental
  providers: [ Passkey ]
   qurilma (barmoq/yuz) bilan kirish — eng xavfsiz (fishing'ga chidamli)

  // 3. DISCORD (yana bir OAuth — o'yin/hamjamiyat ilovalari):
  import Discord from "next-auth/providers/discord";
  Discord({ clientId: process.env.DISCORD_ID!, clientSecret: process.env.DISCORD_SECRET! })

   Email — magic link (parolsiz, email tasdiq); WebAuthn — passkey (qurilma, eng xavfsiz)
   Discord/Google/GitHub — bir xil OAuth naqsh (clientId/secret + callback URL)

Boshqa provayderlar (Email magic link, WebAuthn/passkey, Discord) — OAuth va Credentials'dan tashqari yana uch muhim usul. (1) Email (magic link) — parolsiz autentifikatsiya: foydalanuvchi faqat email kiritadi, unga "sehrli havola" (magic link) yuboriladi, havolani bossa — kirdi (parol umuman yo'q — "email'im borligini isbotladim" degani). Resend yoki Nodemailer provayderi (Resend({ from }) — jo'natuvchi manzil; API kaliti/SMTP env'da). Bu usul VerificationToken jadvalini talab qiladi (adapter — 2.7 — havola tokeni DB'da saqlanadi, bir marta ishlatiladi). Afzalligi — parol yo'q (unutilmaydi, o'g'irlanmaydi), kamchiligi — har kirishda email kutish; (2) WebAuthn / passkey — qurilma xavfsizligi bilan kirish (barmoq izi, Face ID, yoki jismoniy xavfsizlik kaliti). Passkey provayderi (v5'da experimental: { enableWebAuthn: true } bilan). Bu eng xavfsiz usul — fishing (soxta sayt orqali o'g'irlash)ga chidamli (kalit qurilmadan chiqmaydi), parol umuman yo'q; (3) Discord — yana bir OAuth provayder (o'yin, hamjamiyat ilovalarida keng — Google/GitHub bilan bir xil naqsh: clientId/clientSecret + callback URL). Auth.js 80+ tayyor OAuth provayderni qo'llab-quvvatlaydi (Google, GitHub, Discord, Facebook, Apple, Twitter va h.k. — hammasi bir xil Provider({ clientId, clientSecret }) naqshi). Ikki nuqta: (1) Email — magic link (parolsiz, email orqali tasdiq — VerificationToken jadvali kerak), WebAuthn — passkey (qurilma bilan — parolsiz, eng xavfsiz); (2) Discord/Google/GitHub va boshqa OAuth — bir xil naqsh (clientId/clientSecret + provayder console'da callback URL sozlash). Provayder tanlovi UX va xavfsizlikka bog'liq: OAuth (tez), magic link (parolsiz, sodda), passkey (eng xavfsiz — zamonaviy tavsiya), Credentials (to'liq nazorat). Ko'p sayt bir nechtasini birga beradi (foydalanuvchi tanlaydi).

2.12. DB sxema, environment o'zgaruvchilar va token refresh

text
  AUTH.JS STANDART DB SXEMA (adapter uchun — Prisma misolida):

  model User          { id, name, email, emailVerified, image, role, accounts, sessions }
  model Account       { userId, provider, providerAccountId, access_token, refresh_token, ... }
  model Session       { sessionToken, userId, expires }         // database strategiyada
  model VerificationToken { identifier, token, expires }         // magic link uchun

  ENVIRONMENT O'ZGARUVCHILAR (.env — hech qachon git'ga):
  AUTH_SECRET=<npx auth secret>              # token imzosi (majburiy)
  AUTH_URL=https://sayt.com                  # production URL (callback uchun)
  GOOGLE_ID=... / GOOGLE_SECRET=...          # OAuth kalitlari
  GITHUB_ID=... / GITHUB_SECRET=...
  DATABASE_URL=postgresql://...              # adapter DB 6.12-bob

  TOKEN REFRESH (OAuth access_token muddati tugasa — refresh_token bilan yangilash):
  callbacks: { async jwt({ token, account }) {
    if (account) token.refresh_token = account.refresh_token;  // login'da saqla
    // muddat tugasa  provayder token endpoint'iga refresh_token  yangi access_token
  }}

   Account jadvali OAuth token (access/refresh)ni saqlaydi; VerificationToken — magic link
   AUTH_SECRET/AUTH_URL/OAuth kalitlari env'da; refresh_token bilan access_token yangilanadi

DB sxema, environment o'zgaruvchilar va token refresh — adapter ishlashi uchun kerakli poydevor. (1) Auth.js standart DB sxemasi — adapter 2.7-bob to'rt asosiy jadval bilan ishlaydi: User (foydalanuvchi — id, ism, email, emailVerified, rasm, va siz qo'shgan role); Account (OAuth hisoblari — bir User bir nechta provayder bilan bog'lanishi mumkin; provider, providerAccountId, hamda OAuth access_token/refresh_token shu yerda saqlanadi); Session (database strategiyada — sessionToken, userId, expires); VerificationToken (magic link/email tasdiq tokenlari — 2.11). Bu sxemani Auth.js hujjati aynan beradi (Prisma/Drizzle uchun tayyor schema.prisma — nusxa olib prisma migrate — 6.12); (2) Environment o'zgaruvchilar — nozik ma'lumotlar .env faylda (hech qachon git'ga qo'yilmaydi — .gitignore): AUTH_SECRET (token imzosi — majburiy), AUTH_URL (production URL — callback uchun), OAuth kalitlari (GOOGLE_ID/GOOGLE_SECRET va h.k. — provayder console'dan), DATABASE_URL (adapter DB ulanishi). Auth.js v5 ba'zi o'zgaruvchilarni avtomatik taniydi (masalan AUTH_GOOGLE_ID/AUTH_GOOGLE_SECRET prefiksi bilan — qo'lda clientId berish shart emas); (3) Token refresh — OAuth access_token cheksiz emas (masalan Google — 1 soat). Muddati tugagach, provayderga saqlangan refresh_token yuborilib yangi access_token olinadi (agar provayder API'siga so'rov qilib turish kerak bo'lsa — masalan Google Calendar o'qish). Buni jwt callback'da amalga oshiriladi (login'da account.refresh_tokenni saqlab, muddat tekshiruvi bilan yangilash). Oddiy auth (faqat "kim kirgan") uchun refresh kerak emas — u faqat tashqi API'ga uzluksiz kirish kerak bo'lganda muhim. Ikki nuqta: (1) Account jadvali OAuth tokenlarini (access/refresh) saqlaydi, VerificationToken — magic link, Session — database strategiya; (2) AUTH_SECRET/AUTH_URL/OAuth kalitlari env'da (git'siz), refresh_token bilan access_token muddati tugagach yangilanadi. Bu sxema va env sozlamalar — Auth.js'ni real DB va provayderlar bilan ishlashga tayyorlaydi (production poydevori).

2.13. Next.js auth vs alohida backend auth (NestJS JWT — 08-QISM)

text
  IKKI YONDASHUV — auth QAERDA (Next.js'da yoki alohida backend'da):

  A. NEXT.JS AUTH (Auth.js — bu bob):
   Next.js o'zi auth qiladi (fullstack — frontend + auth bir loyihada)
   mos: Next.js — asosiy backend (Server Actions/Route Handlers — DB'ga to'g'ridan)

  B. ALOHIDA BACKEND AUTH (08-QISM NestJS JWT):
   backend (NestJS) JWT beradi; Next.js token'ni saqlab, so'rovlarda yuboradi
   mos: alohida backend bor (mobil + web bir API'ni ulashadi); mikroservis

  INTEGRATSIYA (B holatda):
   NestJS login  JWT  Next.js httpOnly cookie'da saqlaydi (yoki Auth.js Credentials
    provayderi NestJS'ga so'rov qilib, qaytgan tokenni sessiyaga qo'yadi)

   Next.js asosiy backend  Auth.js (bu bob); alohida NestJS API  backend JWT (08-QISM)
   Ikkalasini birlashtirish: Auth.js Credentials  NestJS'ga so'rov  token sessiyaga

Next.js auth vs alohida backend auth (NestJS JWT — 08-QISM) — auth qaerda bo'lishi kerak degan me'moriy savol. Ikki yondashuv: (A) Next.js auth (Auth.js — bu bob) — Next.js o'zi autentifikatsiyani qiladi (fullstack — frontend, auth va API bir loyihada). Bu Next.js asosiy backend bo'lganda mos (Server Actions/Route Handlers to'g'ridan DB bilan ishlaydi — 13.5, 6.12); (B) Alohida backend auth (08-QISM — NestJS JWT) — backend (NestJS) autentifikatsiyani qiladi va JWT beradi, Next.js esa faqat token'ni saqlab, backend so'rovlariga yuboradi. Bu alohida backend bo'lganda mos (masalan bir NestJS API'ni ham web (Next.js), ham mobil ilova ulashadi — auth bir joyda, backend'da; yoki mikroservis arxitektura). Qachon qaysi: agar Next.js loyihaning yagona backend'i bo'lsa — Auth.js (sodda, integratsiyalangan); agar allaqachon alohida API (NestJS/Express) bo'lsa yoki ko'p mijoz (web + mobil) bir API'ni ulashsa — backend auth (08-QISM JWT — markaziy). Integratsiya (ikkalasini birlashtirish): Next.js Auth.js'ning Credentials provayderi ichidan NestJS backend'ining login endpoint'iga so'rov qilib (email/parol NestJS tekshiradi JWT qaytaradi), qaytgan tokenni Auth.js sessiyasiga (jwt callback — 2.6) joylash mumkin. Yoki NestJS bergan JWT'ni Next.js httpOnly cookie'da saqlab (10.11 — cookie xavfsizligi), har backend so'roviga Authorization header'da yuborish. Ikki nuqta: (1) Next.js asosiy backend bo'lsa — Auth.js (bu bob); alohida NestJS/Express API bo'lsa — backend JWT (08-QISM); (2) ikkalasini birlashtirish mumkin (Auth.js Credentials NestJS'ga so'rov tokenni sessiyaga). Tanlov loyiha arxitekturasiga bog'liq (monolit fullstack Next.js vs alohida API + ko'p mijoz). Bu — 08-QISM (NestJS backend auth) va 13-QISM (Next.js auth)ni bog'laydigan me'moriy qaror.

2.14. Muqobil yechimlar (Clerk, Lucia, Supabase Auth)

text
  AUTH.JS'DAN TASHQARI (qisqacha — muqobillar):

  CLERK        to'liq boshqariladigan auth (tayyor UI komponentlar, foydalanuvchi
                paneli, org/team — pullik SaaS; eng kam kod, tez ishga tushirish)
  LUCIA        yengil, kutubxona emas "resept" (to'liq nazorat, o'z sxemang;
                past darajali — ko'proq kod, lekin sehr yo'q, tushunarli)
  SUPABASE     Supabase backend bilan (Postgres + auth + storage bir platformada;
                RLS — row level security bilan integratsiya)

   Auth.js — Next.js standarti (bepul, moslashuvchan); Clerk — tez/pullik; Lucia — past daraja
   Tanlov: tez ishga tushirish  Clerk; to'liq nazorat  Lucia; Supabase stack  Supabase Auth

Muqobil yechimlar (Clerk, Lucia, Supabase Auth) — Auth.js yagona variant emas; qisqacha muqobillar. (1) Clerk — to'liq boshqariladigan (managed) autentifikatsiya xizmati: tayyor UI komponentlar (<SignIn />, <UserButton />), foydalanuvchi boshqaruv paneli, tashkilot/jamoa (org/team), MFA — hammasi tayyor. Eng kam kod bilan tez ishga tushadi, lekin pullik SaaS (foydalanuvchi soniga qarab) va tashqi xizmatga bog'liqlik; (2) Lucia — yengil, "kutubxona emas, reseptlar to'plami" yondashuvi: sessiya boshqaruvini o'zingiz DB sxemangiz bilan quolasiz (to'liq nazorat, "sehr" yo'q — nima bo'layotgani ochiq). Past darajali — ko'proq kod, lekin har qadamni tushunasiz (o'rganish uchun ham yaxshi); (3) Supabase Auth — Supabase platformasi (Postgres + auth + storage + realtime) bilan integratsiyalangan auth. Agar backend sifatida Supabase ishlatsangiz mantiqiy tanlov (auth Postgres RLS — row level security bilan bevosita bog'lanadi). Ikki nuqta: (1) Auth.js — Next.js'ning de-fakto standarti (bepul, ochiq, moslashuvchan — ko'p loyiha uchun to'g'ri tanlov); Clerk — tez, lekin pullik/managed; Lucia — past daraja, to'liq nazorat; (2) tanlov ehtiyojga bog'liq: eng tez ishga tushirish va tayyor UI Clerk; to'liq nazorat va o'rganish Lucia; Supabase stack'da bo'lsangiz Supabase Auth; standart, bepul, Next.js bilan chuqur integratsiya Auth.js (bu bob). Bu bob Auth.js'ga qaratilgan (Next.js ekotizimidagi eng keng tarqalgan tanlov), lekin muqobillarni bilish — loyihaga to'g'ri vositani tanlashda foydali (universal g'oyalar — session, provayder, RBAC — barchasida bir xil).


3. Sintaksis — tez ma'lumotnoma

text
SOZLASH 2.2-bob:     auth.ts  NextAuth({ providers, callbacks, session })
ENDPOINT 2.2-bob:    app/api/auth/[...nextauth]/route.ts  export const { GET, POST } = handlers
PROVAYDER 2.3-bob:   Google({ clientId, clientSecret }) | Credentials({ authorize })
SESSIYA 2.4-bob:     session: { strategy: "jwt" | "database" }
OLISH SERVER 2.5-bob:const session = await auth()
OLISH CLIENT 2.5-bob:const { data: session } = useSession()  (SessionProvider kerak)
CALLBACK 2.6-bob:    callbacks: { jwt({token,user}), session({session,token}) }
ADAPTER 2.7-bob:     adapter: PrismaAdapter(db)
KIRISH 2.8-bob:      signIn("google") | signIn("credentials", {...}) | signOut()
ROL 2.9-bob:         if (session.user.role !== "admin") redirect("/")
PAROL 2.10-bob:      bcrypt.hash(pw, 10) | bcrypt.compare(pw, hash)

4. Batafsil kod namunalari

Har misol: Maqsad + izohli kod + "Bu kod nima qiladi".

Misol 1 — Auth.js asosiy sozlash (OAuth — 2.2, 2.3)

Maqsad: Auth.js'ni Google/GitHub login bilan sozlash — eng oson, eng xavfsiz boshlanish. Bu har Next.js auth loyihasining poydevori.

tsx
// auth.ts — markaziy sozlama (loyiha ildizida)
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
import GitHub from "next-auth/providers/github";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { db } from "@/lib/db";

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(db),          // foydalanuvchini DB'da saqla (2.7)
  providers: [
    Google({ clientId: process.env.GOOGLE_ID!, clientSecret: process.env.GOOGLE_SECRET! }),
    GitHub({ clientId: process.env.GITHUB_ID!, clientSecret: process.env.GITHUB_SECRET! }),
  ],
  session: { strategy: "jwt" },        // JWT sessiya (2.4)
  pages: { signIn: "/login" },         // maxsus login sahifasi (default emas)
});

// app/api/auth/[...nextauth]/route.ts — Auth.js endpoint:
// import { handlers } from "@/auth";
// export const { GET, POST } = handlers;

Bu kod nima qiladi: Bu — Auth.js'ning asosiy sozlamasi (OAuth login bilan — 2.2, 2.3). auth.ts (markaziy fayl) NextAuth({...}) chaqiradi va to'rt narsani export qiladi: handlers (auth endpoint), auth (sessiyani olish — 2.5), signIn/signOut (kirish/chiqish — 2.8). Sozlamalar: (1) adapter: PrismaAdapter(db) — foydalanuvchilarni DB'da saqlaydi (2.7 — yangi login DB'da User; OAuth hisoblari bog'lanadi); (2) providers — ikki OAuth: Google va GitHub (har biri clientId/clientSecret — provayder console'dan olingan, env'da — secret server'da — 2.3); (3) session: { strategy: "jwt" } — JWT sessiya (token cookie'da — tez — 2.4); (4) pages: { signIn: "/login" } — maxsus login sahifasi (Auth.js default sahifasi o'rniga o'z dizayningiz). Va app/api/auth/[...nextauth]/route.tsda handlersni export qilinadi (Auth.js o'z route'ini — OAuth oqimi, callback — boshqaradi — 2.2). Endi ilovangizda autentifikatsiya tayyor: foydalanuvchi "Google bilan kirish" bossa — Google'ga yo'naltiriladi, tasdiqlab qaytadi, Auth.js sessiya yaratadi (DB'da User, JWT token), va u kirgan hisoblanadi. Bu — minimal, lekin to'liq ishlaydigan auth sozlamasi (OAuth — eng oson, eng xavfsiz — parol siz saqlamaysiz, Google boshqaradi). Real loyiha shundan boshlaydi (Google/GitHub login — bir necha qator bilan to'liq autentifikatsiya). Auth.js murakkab ishni (OAuth oqimi, sessiya, CSRF) yashiradi — siz sozlamani berasiz.

Misol 2 — Credentials (email/parol — 2.3, 2.10)

Maqsad: Email/parol bilan login qilish — bcrypt parol tekshiruvi bilan. Bu an'anaviy login uchun, xavfsizlik (hash) bilan.

tsx
// auth.ts — Credentials provayder
import Credentials from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import { z } from "zod";

const LoginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(6),
});

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Credentials({
      authorize: async (credentials) => {
        // 1. Validatsiya (Zod — kirish ma'lumotini tekshir):
        const parsed = LoginSchema.safeParse(credentials);
        if (!parsed.success) return null;   // noto'g'ri format  rad

        // 2. Foydalanuvchini topish:
        const user = await db.user.findUnique({ where: { email: parsed.data.email } });
        if (!user || !user.passwordHash) return null;   // yo'q  rad

        // 3. Parolni tekshir (bcrypt — hash bilan — 2.10):
        const valid = await bcrypt.compare(parsed.data.password, user.passwordHash);
        if (!valid) return null;            // noto'g'ri parol  rad

        return { id: user.id, email: user.email, name: user.name, role: user.role };  //  kirdi
      },
    }),
  ],
  session: { strategy: "jwt" },
});

Bu kod nima qiladi: Bu — email/parol bilan login (Credentials — an'anaviy, xavfsizlik bilan — 2.3, 2.10). Credentials provayderining authorize funksiyasi foydalanuvchi kiritgan email/parolni tekshiradi (uch qadam): (1) validatsiya — Zod bilan (LoginSchema.safeParse — email to'g'ri formatda'mi, parol kamida 6 belgimi — 13.5: 2.9); noto'g'ri bo'lsa null (rad); (2) foydalanuvchini topishdb.user.findUnique (email bo'yicha); topilmasa yoki paroli yo'q (OAuth bilan ro'yxatdan o'tgan — paroli yo'q) bo'lsa null (rad); (3) parolni tekshirbcrypt.compare(kiritilgan parol, user.passwordHash) (2.10 — foydalanuvchi kiritgan parolni DB'dagi hash bilan solishtiradi — bcrypt hash'ni teskari ocha olmaydi, lekin solishtira oladi); noto'g'ri bo'lsa null (rad); to'g'ri bo'lsa foydalanuvchi ma'lumotini (id, email, name, role — rol callbacks uchun — 2.6) qaytaradi (kirdi). Eng muhim xavfsizlik: parol DB'da hash holda saqlanadi (ochiq emas — 2.10) — authorizeda bcrypt.compare bilan solishtiriladi (kiritgan parolni hash qilib, saqlangan hash bilan taqqoslaydi). Agar ochiq parol solishtirsangiz (if (password === user.password)) — bu xavfli (DB sizsa barcha parol ochiq). authorize null qaytarsa — Auth.js login'ni rad etadi (foydalanuvchiga "noto'g'ri email yoki parol"). Bu — an'anaviy email/parol login'ning xavfsiz amali (validatsiya + foydalanuvchi topish + bcrypt). Ro'yxatda (Misol kerak bo'lsa) parol bcrypt.hash(password, 10) bilan hash qilib saqlanadi. Credentials — to'liq nazorat, lekin xavfsizlik mas'uliyati sizda (OAuth'da Google boshqaradi — Misol 1 — osonroq).

Misol 3 — Sessiyani server'da olish (himoyalangan sahifa — 2.5, 2.9)

Maqsad: Himoyalangan sahifani server'da sessiya bilan himoyalash — kirmagan foydalanuvíni login'ga. Bu eng keng auth naqshi (13.2: Misol 8'ning to'liq versiyasi).

tsx
// app/dashboard/page.tsx — himoyalangan (Server Component)
import { auth } from "@/auth";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const session = await auth();   //  server'da sessiya (2.5)

  // Kirmagan  login'ga (server'da — xavfsiz, tez — 13.2: Misol 8):
  if (!session) {
    redirect("/login");
  }

  // Bu yerga faqat KIRGAN foydalanuvchi yetadi:
  return (
    <main>
      <h1>Dashboard</h1>
      <p>Salom, {session.user.name}!</p>
      <p>Email: {session.user.email}</p>
      {/* Rolga qarab (avtorizatsiya — 2.9): */}
      {session.user.role === "admin" && <AdminLink />}
    </main>
  );
}

Bu kod nima qiladi: Bu — himoyalangan sahifa (server'da sessiya bilan — eng keng auth naqshi — 2.5, 2.9). DashboardPage (Server Component) server'da render bo'lishidan oldin sessiyani tekshiradi: const session = await auth() (2.5 — server'da sessiya — session.user mavjud bo'lsa kirgan, null bo'lsa kirmagan). Agar !session (kirmagan) — redirect("/login") (server'da login'ga yo'naltiradi — komponent render to'xtaydi — himoyalangan kontent brauzerga umuman yuborilmaydi — 13.2: Misol 8). Agar sessiya bor — sahifa davom etadi, foydalanuvchi ma'lumotini ko'rsatadi (session.user.name, email), va rolga qarab qo'shimcha (session.user.role === "admin" && <AdminLink /> — admin uchun qo'shimcha havola — avtorizatsiya — 2.9). Nega server'da (Client guard o'rniga — 11.15): (1) xavfsiz — himoyalangan kontent brauzerga umuman yuborilmaydi (Client guard'da kontent yuboriladi, keyin yashiriladi — texnik jihatdan ko'riladi — 13.2: Misol 8); (2) tez — server darrov yo'naltiradi (JavaScript yuklanishini kutmacdan). Bu — Next.js'ning eng keng auth naqshi: har himoyalangan sahifa boshida const session = await auth(); if (!session) redirect("/login"). Rolga qarab (admin) — avtorizatsiya (2.9 — rol sessiyaga callbacks orqali qo'shilgan — 2.6). Eslatma: bu faqat UI himoyasi — agar sahifada Server Action bo'lsa, u ham alohida tekshirilishi shart (2.9, Misol 5 — ochiq endpoint). Server-first auth (13.3 falsafasi auth'da) — server'da tekshirish, xavfsiz, tez. Bu naqsh har himoyalangan sahifada takrorlanadi (dashboard, profil, sozlamalar).

Misol 4 — Middleware bilan himoya (markaziy — 2.9, 13.6)

Maqsad: Barcha himoyalangan yo'llarni markaziy himoyalash — Auth.js middleware bilan. Bu har sahifada guard yozishdan qutqaradi (13.6: Misol 4'ning Auth.js versiyasi).

tsx
// middleware.ts — Auth.js bilan markaziy himoya
import { auth } from "@/auth";

export default auth((req) => {
  const { pathname } = req.nextUrl;
  const isLoggedIn = !!req.auth;            //  Auth.js sessiyani req.auth'ga qo'shadi
  const role = req.auth?.user?.role;

  // 1. Himoyalangan yo'l + kirmagan  login:
  if (pathname.startsWith("/dashboard") && !isLoggedIn) {
    return Response.redirect(new URL("/login", req.url));
  }

  // 2. Admin yo'l + admin emas  bosh sahifa (avtorizatsiya — 2.9):
  if (pathname.startsWith("/admin") && role !== "admin") {
    return Response.redirect(new URL("/", req.url));
  }
});

// Qaysi yo'llapga (matcher — 13.6: 2.8):
export const config = {
  matcher: ["/dashboard/:path*", "/admin/:path*"],
};

Bu kod nima qiladi: Bu — markaziy auth himoyasi (Auth.js middleware — 13.6: Misol 4'ning Auth.js bilan to'liq versiyasi). Auth.js auth funksiyasini middleware sifatida ishlatadi: export default auth((req) => {...}) — Auth.js sessiyani avtomatik req.authga qo'shadi (!!req.auth — kirgan'mi, req.auth?.user?.role — rol). Ikki qoida: (1) himoyalangan yo'l + kirmagan/dashboardga kirmagan odam kelsa, login'ga yo'naltiradi (autentifikatsiya); (2) admin yo'l + admin emas/adminga admin bo'lmagan foydalanuvchi (hatto kirgan, lekin oddiy) kelsa, bosh sahifaga yo'naltiradi (avtorizatsiya — rol tekshir — 2.9). matcher (["/dashboard/:path*", "/admin/:path*"]) — middleware faqat shu yo'llarga ishlaydi (13.6: 2.8 — performance). Nega middleware (har sahifada guard o'rniga — Misol 3): markaziy — bir joyda butun himoya (har /dashboard/* va /admin/* sahifaga alohida guard yozish shart emas), va tezroq (edge'da — sahifa render bo'lishidan oldin — 13.6: 2.9). Auth.js middleware integratsiyasi (auth((req) => ...)) buni juda soddalashtiradi (req.auth — sessiya avtomatik — qo'lda cookie o'qish yo'q — 13.6: Misol 4'dagi qo'lda token o'qishdan farqli). Demak: middleware (markaziy — /dashboard, /admin — Misol 4) + Server Component tekshiruvi (Misol 3 — qo'shimcha qatlam) + Server Action/API tekshiruvi (Misol 5 — ochiq endpoint) = chuqur himoya (defense in depth — 2.9). Middleware — birinchi himoya qatlami (markaziy, tez), lekin yagona emas (har Server Action/API ham o'zini himoya qiladi). Bu — Auth.js + Next.js middleware integratsiyasining kuchi (markaziy auth — kam kod, yuqori himoya).

Misol 5 — Server Action avtorizatsiya (rol — 2.9)

Maqsad: Server Action'ni avtorizatsiya bilan himoyalash — faqat admin foydalanuvchi bajara olishi. Bu UI himoyasidan ham muhim (ochiq endpoint — 13.5: 2.11).

tsx
// app/admin/actions.ts — admin Server Actionlari
"use server";
import { auth } from "@/auth";
import { revalidatePath } from "next/cache";

// Yordamchi — admin tekshiruvi (qayta ishlatiladi):
async function requireAdmin() {
  const session = await auth();
  if (!session) throw new Error("Tizimga kiring");
  if (session.user.role !== "admin") throw new Error("Faqat admin uchun");  //  rol
  return session;
}

export async function deleteUser(userId: string) {
  await requireAdmin();   //  HAR Server Action'da tekshir (ochiq endpoint — 13.5: 2.11)
  await db.user.delete({ where: { id: userId } });
  revalidatePath("/admin/users");
}

export async function banUser(userId: string) {
  await requireAdmin();   // takror tekshir (har action mustaqil himoyalangan)
  await db.user.update({ where: { id: userId }, data: { banned: true } });
  revalidatePath("/admin/users");
}

Bu kod nima qiladi: Bu — Server Action avtorizatsiyasi (rol bilan — UI himoyasidan ham muhim — 2.9, 13.5: 2.11). requireAdmin yordamchi funksiya: const session = await auth(); if (!session) throw...; if (session.user.role !== "admin") throw... — ikki tekshiruv (autentifikatsiya — kirgan'mi; avtorizatsiya — admin'mi), tekshiryvdan o'tsa sessiyani qaytaradi (qayta ishlatiladigan — DRY — har admin action'da takrorlamacdan). Har admin Server Action (deleteUser, banUser) birinchi qadam — await requireAdmin() (tekshirish, keyin amal). Nega bu UI himoyasidan ham muhim (Misol 3 — sahifa admin'ni yashiradi): Server Action ochiq endpoint (13.5: 2.11) — agar admin sahifani yashirsangiz ham (UI), deleteUser Server Action'i rol tekshirmasa, oddiy foydalanuvchi uni to'g'ridan chaqirib (brauzer devtools, yoki HTTP so'rov), foydalanuvchi o'chira oladi (jiddiy xavfsizlik teshigi). UI yashirish — faqat ko'rinish (oddiy foydalanuvchí "O'chirish" tugmasini ko'rmaydi), lekin haqiqiy himoya — Server Action'da (kim chaqirsa ham, rol tekshiriladi). Har action mustaqil himoyalangan (requireAdmin har birida — biri ikkinchisiga ishonmaydi). Bu — chuqur himoya (defense in depth — 2.9, 14-QISM): UI (Misol 3) + middleware (Misol 4) + Server Action (bu Misol) — har qatlam o'zini himoya qiladi (biror qatlam aylanib o'tilsa — keyingi qatlam ushlaydi). Eng keng xato — faqat UI yoki middleware'da tekshirib, Server Action'ni himoyalamaslik (ochiq qoldirish — har kim chaqira oladi). To'g'ri: har himoyalangan Server Action o'z avtorizatsiya tekshiruvi bilan. Bu — xavfsiz mutation'ning asosi (server'da bo'lgani yetarli emas — kim chaqirayotganini tekshir).

Misol 6 — Client'da sessiya (navbar — 2.5)

Maqsad: Navbar'da foydalanuvchi holatiga qarab UI ko'rsatish (kirgan/kirmagan) — useSession bilan. Bu interaktiv komponentda client sessiyasi kerak bo'lgan holat.

tsx
// app/components/Navbar.tsx — Client (foydalanuvchi holatiga qarab)
"use client";
import { useSession, signIn, signOut } from "next-auth/react";
import Link from "next/link";

export default function Navbar() {
  const { data: session, status } = useSession();   // client'da sessiya (2.5)

  return (
    <nav>
      <Link href="/">Bosh</Link>
      {status === "loading" ? (
        <span>...</span>                              // sessiya yuklanmoqda
      ) : session ? (
        // KIRGAN — profil + chiqish:
        <>
          <span>Salom, {session.user?.name}</span>
          <Link href="/dashboard">Dashboard</Link>
          <button onClick={() => signOut()}>Chiqish</button>
        </>
      ) : (
        // KIRMAGAN — kirish:
        <button onClick={() => signIn()}>Kirish</button>
      )}
    </nav>
  );
}

// app/layout.tsx — SessionProvider (useSession uchun — 2.5):
// import { SessionProvider } from "next-auth/react";
// <SessionProvider><Navbar />{children}</SessionProvider>

Bu kod nima qiladi: Bu — client'da sessiya (navbar — interaktiv komponentda — 2.5). Navbar — Client Component ("use client" — foydalanuvchi holatiga qarab interaktiv o'zgaradi). useSession() — client'da sessiyani oladi: data: session (foydalanuvchi ma'lumoti yoki null), status ("loading"/"authenticated"/"unauthenticated"). Navbar uch holatda: (1) status === "loading" — sessiya hali yuklanmoqda (... — bir lahzali); (2) session bor (kirgan) — foydalanuvchi nomi, dashboard havolasi, "Chiqish" tugmasi (signOut()); (3) session yo'q (kirmagan) — "Kirish" tugmasi (signIn()). useSession ishlashi uchun SessionProvider kerak (layout'da butun ilovani o'rab — Context orqali sessiyani tarqatadi — 2.5, 12.1). Nega client'da (server auth() o'rniga — Misol 3): navbar — interaktiv (foydalanuvchi kirsa/chiqsa darrov o'zgarishi kerak — masalan "Chiqish" bossa, navbar darrov "Kirish"ga o'zgaradi — sahifani qata yuklamacdan), va u ko'p sahifada ko'rinadi (umumiy — layout'da). useSession client'da sessiyani "jonli" boshqaradi (kirish/chiqish darrov aks etadi). Server'da auth() (Misol 3) — bir martalik tekshiruv (sahifa render paytida), client'da useSession — uzluksiz (interaktiv). Qachon qaysi 2.5-bob: server auth() — sahifa himoyasi (tekshir redirect — bir marta); client useSession — interaktiv UI (navbar, foydalanuvchi holatiga qarab — uzluksiz). Ko'p ilovada ikkalasi: server'da himoya (Misol 3), client'da interaktiv navbar (bu Misol). SessionProvider faqat client useSession uchun kerak (server auth() provayder talab qilmaydi). Bu — auth'ning client tomoni (interaktiv foydalanuvchi holati).

Misol 7 — Ro'yxatdan o'tish (signup — bcrypt — 2.10)

Maqsad: Yangi foydalanuvi ro'yxatdan o'tkazish — parolni bcrypt bilan hash qilib saqlash. Bu Credentials login (Misol 2)ning jufti (ro'yxat login).

tsx
// app/register/actions.ts — ro'yxatdan o'tish (Server Action)
"use server";
import bcrypt from "bcryptjs";
import { z } from "zod";
import { redirect } from "next/navigation";

const RegisterSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  password: z.string().min(6, "Parol kamida 6 belgi"),
});

export async function register(prevState: any, formData: FormData) {
  // 1. Validatsiya (Zod):
  const parsed = RegisterSchema.safeParse(Object.fromEntries(formData));
  if (!parsed.success) return { error: parsed.error.issues[0].message };

  const { name, email, password } = parsed.data;

  // 2. Email band emasligini tekshir:
  const existing = await db.user.findUnique({ where: { email } });
  if (existing) return { error: "Bu email allaqachon ro'yxatdan o'tgan" };

  // 3. Parolni HASH qilib saqla (HECH QACHON ochiq — 2.10):
  const passwordHash = await bcrypt.hash(password, 10);   // 10 — salt rounds
  await db.user.create({
    data: { name, email, passwordHash, role: "user" },    // hash saqlanadi, ochiq parol YO'Q
  });

  redirect("/login?registered=true");   // ro'yxatdan keyin login'ga
}

Bu kod nima qiladi: Bu — ro'yxatdan o'tish (signup — parol hash bilan — 2.10; Misol 2 Credentials login'ning jufti). register Server Action (forma bilan — 13.5): (1) validatsiya — Zod (RegisterSchema.safeParse — ism, email, parol to'g'rimi — Object.fromEntries(formData) formani obyektga aylantiradi); noto'g'ri bo'lsa xato qaytaradi; (2) email band emasligini tekshirdb.user.findUnique (email bo'yicha); agar mavjud bo'lsa xato ("allaqachon ro'yxatdan o'tgan" — bir email bir marta); (3) parolni hash qilib saqlabcrypt.hash(password, 10) (2.10 — parolni hash qiladi — 10 salt rounds — qancha "qiyin" — yuqori = xavfsizroq, lekin sekinroq), keyin db.user.create bilan hashni saqlaydi (passwordHashochiq parol hech qaerda saqlanmaydi), va role: "user" (default rol). Keyin redirect("/login?registered=true") (ro'yxatdan keyin login sahifasiga — ?registered=true bilan "muvaffaqiyatli ro'yxatdan o'tdingiz" xabari uchun). Eng muhim xavfsizlik 2.10-bob: parol hech qachon ochiq saqlanmaydibcrypt.hash bilan bir tomonlama (irreversible) hash qilinadi (hash'dan parolni teskari ocha bo'lmaydi). Login'da (Misol 2) bcrypt.compare bilan tekshiriladi (kiritgan parolni hash qilib, saqlangan hash bilan solishtirish). Agar ochiq parol saqlasangiz va DB sizsa — barcha foydalanuvchi paroli ochiq (falokat — odamlar o'sha parolni boshqa saytlarda ham ishlatadi). Bu — ro'yxat login juftining ro'yxat qismi (Credentials uchun). Email band emasligini tekshirish (takror oldini), parol hash (xavfsizlik), validatsiya (Zod — to'g'ri ma'lumot) — uchalasi ham muhim. OAuth (Misol 1)da ro'yxat avtomatik (Google boshqaradi — parol yo'q), Credentials'da esa siz qilasiz (bu Misol). Parol hash — autentifikatsiya xavfsizligining eng asosiy qoidasi.

Misol 8 — Rol bilan callback (sessiyaga rol — 2.6)

Maqsad: Foydalanuvchi rolini sessiyaga qo'shish — callbacklar orqali. Bu avtorizatsiya (Misol 3, 4, 5)ning asosi (rol sessiyada bo'lishi shart).

tsx
// auth.ts — callbacks (rolni sessiyaga olib chiqish)
export const { handlers, auth } = NextAuth({
  providers: [/* ... */],
  session: { strategy: "jwt" },
  callbacks: {
    // jwt — token yaratilganda (login) rolni DB'dan token'ga:
    async jwt({ token, user }) {
      if (user) {                          // birinchi marta (login)
        token.role = user.role;            //  rol token'da saqlanadi
        token.id = user.id;
      }
      return token;
    },
    // session — token'dagi rolni sessiyaga (komponentda ishlatish uchun):
    async session({ session, token }) {
      if (session.user) {
        session.user.role = token.role as string;   //  endi session.user.role bor
        session.user.id = token.id as string;
      }
      return session;
    },
  },
});

// TypeScript — sessiya tipini kengaytirish (types/next-auth.d.ts):
// declare module "next-auth" {
//   interface Session { user: { id: string; role: string } & DefaultSession["user"] }
// }

Bu kod nima qiladi: Bu — rolni sessiyaga qo'shish (callbacklar — avtorizatsiyaning asosi — 2.6). Default'da Auth.js sessiyaga faqat name/email/image qo'yadi — lekin avtorizatsiya (Misol 3, 4, 5 — session.user.role)ning ishlashi uchun rol sessiyada bo'lishi kerak. Buni callbacklar qiladi: (1) jwt callback — token yaratilganda (login paytida — user mavjud — DB'dagi foydalanuvchi): token.role = user.role; token.id = user.id — rolni va ID'ni token'ga qo'shadi (token cookie'da — keyingi so'rovlarda DB'siz o'qiladi — JWT strategiya — 2.4); (2) session callback — sessiya o'qilganda: session.user.role = token.role — token'dagi rolni sessiyaga ko'chiradi (endi session.user.role mavjud — har joyda — Misol 3, 4, 5'da ishlatilgan). Natija: const session = await auth(); session.user.role — avtorizatsiya uchun rol mavjud (callbacklarsiz — undefined — avtorizatsiya ishlamasdi). Va TypeScript — sessiya tipini kengaytirish (types/next-auth.d.tsinterface Sessionga role qo'shish) — TypeScript session.user.roleni taniydi (xato bermaydi — 11.14). Nega ikki callback: jwt (token'ga — bir marta, login paytida — DB'dan rol olib token'ga solish), session (token'dan sessiyaga — har o'qishda — komponentga berish). JWT strategiyada 2.4-bob rol token'da (DB'siz o'qiladi — tez), database strategiyada esa boshqacharoq (DB'dan). Bu — autentifikatsiya (Auth.js — kim) va avtorizatsiya (rol — 2.1)ni bog'laydigan kalit qadam (rol sessiyaga qo'shilgach, har joyda (Misol 3, 4, 5) ruxsat tekshirsa bo'ladi). Callbacksiz rol-asosli avtorizatsiya ishlamaydi (rol sessiyada yo'q). Bu — Auth.js'ni o'z ilovangizning ruxsat tizimiga moslashtirishning asosi.

Maqsad: Adapter uchun DB sxemasini ko'rsatish va parolsiz provayderlarni (magic link, passkey) sozlash. Bu OAuth va Credentials'dan tashqari zamonaviy, xavfsiz login usullari.

prisma
// prisma/schema.prisma — Auth.js standart sxema (2.7, 2.12)
model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  role          String    @default("user")   //  avtorizatsiya uchun (2.6)
  passwordHash  String?                       // Credentials uchun (2.3, 2.10)
  accounts      Account[]
  sessions      Session[]
}
model Account {              // OAuth hisoblari (access_token, refresh_token — 2.12)
  userId            String
  provider          String
  providerAccountId String
  refresh_token     String?  @db.Text
  access_token      String?  @db.Text
  user              User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  @@id([provider, providerAccountId])
}
model Session {              // database strategiyada (2.4)
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model VerificationToken {    // magic link / email tasdiq (2.11)
  identifier String
  token      String   @unique
  expires    DateTime
  @@unique([identifier, token])
}
tsx
// auth.ts — parolsiz provayderlar (magic link + passkey — 2.11)
import NextAuth from "next-auth";
import Resend from "next-auth/providers/resend";
import Passkey from "next-auth/providers/passkey";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { db } from "@/lib/db";

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(db),                    // VerificationToken kerak (magic link)
  experimental: { enableWebAuthn: true },        // WebAuthn/passkey yoqish (v5)
  providers: [
    Resend({ from: "no-reply@sayt.com" }),       // magic link — email'ga havola
    Passkey,                                      // barmoq izi / Face ID / xavfsizlik kaliti
  ],
});

Bu kod nima qiladi: Bu — adapter DB sxemasi (2.7, 2.12) va parolsiz provayderlar 2.11-bob. Prisma sxema — Auth.js adapter to'rt jadval bilan ishlaydi: User (foydalanuvchi — bu yerga role (avtorizatsiya — 2.6) va passwordHash (Credentials — 2.10) qo'shilgan), Account (OAuth hisoblari — access_token/refresh_token shu yerda — 2.12; @@id([provider, providerAccountId]) — bir provayder hisobi bir marta), Session (database strategiyada — 2.4), VerificationToken (magic link tokenlari — 2.11). Bu sxema Auth.js hujjatidan olinadi (nusxa prisma migrate jadvallar yaratiladi — 6.12). auth.ts — ikki parolsiz provayder: (1) Resend (magic link) — foydalanuvchi email kiritsa, unga havola yuboriladi (from — jo'natuvchi; Resend API kaliti env'da), havolani bossa kirdi (parol yo'q — VerificationToken jadvali havolani boshqaradi); (2) Passkey (WebAuthn — experimental: { enableWebAuthn: true } bilan) — qurilma (barmoq izi, Face ID) yoki xavfsizlik kaliti bilan kirish (eng xavfsiz — fishing'ga chidamli). Bu — zamonaviy, parolsiz autentifikatsiya (parol boshqaruvining og'irligi va xavfini yo'qotadi). OAuth (Misol 1) va Credentials (Misol 2)dan farqli — bu yerda foydalanuvchi parol eslab qolmaydi (magic link — email; passkey — qurilma). Ko'p zamonaviy sayt shu tomonga o'tmoqda (passkey — parolning kelajakdagi o'rnini bosuvchi). Sxema va provayder birga — Auth.js'ning to'liq ma'lumot qatlami (DB + login usullari).

Misol 8c — signIn callback + xato sahifasi (kirishni cheklash — 2.6, sahifalar)

Maqsad: signIn callback bilan kimlar kira olishini cheklash va maxsus xato sahifasini ko'rsatish. Bu kirish siyosatini (masalan bloklangan foydalanuvchini rad etish) markaziy boshqarish uchun.

tsx
// auth.ts — signIn callback + maxsus sahifalar
export const { handlers, auth } = NextAuth({
  providers: [/* Google, GitHub — Misol 1 */],
  pages: {
    signIn: "/login",          // maxsus login sahifasi (Misol 9)
    error: "/auth/error",      //  maxsus xato sahifasi (default o'rniga)
  },
  callbacks: {
    // signIn — kirishga RUXSAT berishdan oldin tekshir (true  kirsin, false  rad):
    async signIn({ user, account }) {
      // 1. Faqat tasdiqlangan email domenlari (masalan korporativ ilova):
      if (account?.provider === "google" && !user.email?.endsWith("@kompaniya.uz")) {
        return false;          // rad  error sahifasiga (AccessDenied)
      }
      // 2. Bloklangan foydalanuvchini rad et:
      const dbUser = await db.user.findUnique({ where: { email: user.email! } });
      if (dbUser?.banned) return false;
      return true;             // ruxsat  kirsin
    },
  },
});

Bu kod nima qiladi: Bu — signIn callback (kirish siyosati) va maxsus xato sahifasi. pages — Auth.js standart sahifalari o'rniga o'z sahifalaringiz: signIn: "/login" (login — Misol 9), error: "/auth/error" (xato sahifasi — masalan OAuth xatosi, kirish rad etilganda ko'rsatiladi — ?error=AccessDenied query bilan). signIn callback — bu foydalanuvchi login qilishga urinsa, Auth.js sessiya yaratishdan oldin chaqiriladi va true/false qaytaradi (true — kirsin, false — rad — error sahifasiga). Ikki misol: (1) domen cheklovi — masalan korporativ ilovada faqat @kompaniya.uz email'li Google hisoblari kira olsin (boshqasi rad); (2) bloklangan foydalanuvchi — DB'da banned bo'lsa rad et (u sessiya ololmaydi). Farqi (Credentials authorizedan — 2.3): authorize faqat Credentials'da parol tekshiradi, signIn callback esa barcha provayderlar (OAuth ham) uchun ishlaydi — yagona, markaziy kirish siyosati. Bu — kimlar tizimga kira olishini nazorat qilish (autentifikatsiyadan ham oldingi filtr — masalan taklif orqali ro'yxat, domen cheklovi, ban). Maxsus xato sahifasi esa foydalanuvchiga tushunarli xabar beradi (Auth.js standart sahifasi o'rniga — brend, til, dizayn sizniki). Bu callbacklar 2.6-bob va sahifalar (pages) — Auth.js'ni kirish siyosati va UX bo'yicha to'liq moslashtiradi.

Misol 9 — To'liq login sahifasi (UI + Server Action — 2.8)

Maqsad: To'liq login sahifani qurish — OAuth tugmalari + email/parol formasi. Bu foydalanuvi ko'radigan real auth interfeysi.

tsx
// app/login/page.tsx — login sahifasi (server)
import { signIn } from "@/auth";

export default function LoginPage() {
  return (
    <div className="login">
      <h1>Kirish</h1>

      {/* 1. OAUTH (Server Action — forma — 2.8): */}
      <form action={async () => { "use server"; await signIn("google", { redirectTo: "/dashboard" }); }}>
        <button>Google bilan kirish</button>
      </form>
      <form action={async () => { "use server"; await signIn("github", { redirectTo: "/dashboard" }); }}>
        <button>GitHub bilan kirish</button>
      </form>

      <div className="divider">yoki</div>

      {/* 2. CREDENTIALS (email/parol — Server Action): */}
      <form action={async (formData: FormData) => {
        "use server";
        await signIn("credentials", {
          email: formData.get("email"),
          password: formData.get("password"),
          redirectTo: "/dashboard",
        });
      }}>
        <input name="email" type="email" placeholder="Email" required />
        <input name="password" type="password" placeholder="Parol" required />
        <button type="submit">Kirish</button>
      </form>

      <a href="/register">Hisobingiz yo'qmi? Ro'yxatdan o'ting</a>
    </div>
  );
}

Bu kod nima qiladi: Bu — to'liq login sahifasi (foydalanuvchi ko'radigan real auth interfeysi — 2.8). Uch login usuli, hammasi Server Action (forma — progressive enhancement — 13.5: 2.5): (1) Google OAuth<form action={async () => { "use server"; await signIn("google", { redirectTo: "/dashboard" }) }}> (Google bilan kirish tugmasi — bosilsa Google'ga yo'naltiriladi, tasdiqlab qaytgach dashboard'ga — redirectTo); (2) GitHub OAuth — xuddi shunday (signIn("github")); (3) Credentials (email/parol) — forma formData bilan: signIn("credentials", { email: formData.get("email"), password: ..., redirectTo: "/dashboard" }) — email/parol Auth.js'ning authorize funksiyasiga boradi (Misol 2 — bcrypt tekshiruvi), to'g'ri bo'lsa dashboard'ga. Va "Ro'yxatdan o'ting" havolasi (yangi foydalanuvchi uchun — Misol 7). Nega Server Action (client signIn o'rniga): progressive enhancement (forma JavaScript'siz ham ishlaydi — 13.5: 2.5 — sekin internet, JS yuklanmagan holda ham login mumkin — ishonchli, accessibility), sodda (alohida client state yo'q). Bu — real saytlarning login sahifasi (Google/GitHub — tez, parolsiz; email/parol — an'anaviy — foydalanuvchi tanlaydi). Ko'p usul berish yaxshi UX (foydalanuvchi qulayini tanlaydi — ba'zi odamlar Google'ni afzal ko'radi — parol eslab qolish yo'q, ba'zilari email/parol — nazorat). signIn Auth.js'ning kirish funksiyasi (OAuth oqimi yoki credentials tekshiruvi — avtomatik). Bu — autentifikatsiya interfeysining (foydalanuvchí ko'radigan qism) to'liq namunasi. Dizayn (CSS) qo'shilsa — professional login sahifa.

Misol 10 — To'liq auth tizimi (hammasi birga)

Maqsad: To'liq autentifikatsiya tizimini ko'rsatish — sozlash, himoya, rol, har qatlam. Bu butun bobning amaliy yakuni (production auth arxitekturasi).

tsx
// === 1. auth.ts (sozlama — Misol 1, 2, 8) ===
export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(db),
  providers: [Google, Credentials({ authorize: /* bcrypt — Misol 2 */ })],
  session: { strategy: "jwt" },
  callbacks: { jwt: /* rol — Misol 8 */, session: /* rol — Misol 8 */ },
  pages: { signIn: "/login" },
});

// === 2. middleware.ts (markaziy himoya — Misol 4) ===
export default auth((req) => {
  if (req.nextUrl.pathname.startsWith("/dashboard") && !req.auth) /*  login */;
  if (req.nextUrl.pathname.startsWith("/admin") && req.auth?.user?.role !== "admin") /*  / */;
});

// === 3. Server Component (sahifa himoya — Misol 3) ===
// const session = await auth(); if (!session) redirect("/login");

// === 4. Server Action (avtorizatsiya — Misol 5) ===
// "use server"; await requireAdmin(); /* amal */

// === 5. Client (navbar — Misol 6) ===
// "use client"; const { data: session } = useSession();

// HIMOYA QATLAMLARI (defense in depth — 2.9):
// Middleware (markaziy, tez)  Server Component (sahifa)  Server Action (mutation)
//  har qatlam mustaqil himoyalangan (biri aylanib o'tilca — keyingi ushlaydi)

Bu kod nima qiladi: Bu — butun bobning amaliy yakuni (to'liq, production auth arxitekturasi — barcha qism birga). Besh komponent: (1) auth.ts (sozlama — Misol 1, 2, 8) — provayderlar (Google OAuth + Credentials bcrypt bilan), JWT sessiya, rol callbacklari (sessiyaga rol — avtorizatsiya uchun), maxsus login sahifasi; (2) middleware.ts (markaziy himoya — Misol 4) — /dashboard (kirgan kerak — autentifikatsiya), /admin (admin kerak — avtorizatsiya) — birinchi, tez qatlam; (3) Server Component (sahifa himoyasi — Misol 3) — auth() bilan sessiyani tekshirib, kirmaganni redirect; (4) Server Action (avtorizatsiya — Misol 5) — requireAdmin() har mutation'da (ochiq endpoint — 13.5: 2.11); (5) Client (navbar — Misol 6) — useSession bilan interaktiv UI. Eng muhim g'oya — himoya qatlamlari (defense in depth — chuqur himoya — 2.9, 14-QISM): middleware (markaziy, tez — birinchi) Server Component (sahifa — ikkinchi) Server Action (mutation — uchinchi) — har qatlam mustaqil himoyalangan (biri aylanib o'tilsa — keyingi ushlaydi). Masalan: agar kimdir middleware'ni aylanib o'tsa (texnik usul bilan), Server Component uni ushlaydi; agar sahifani aylanib o'tsa (Server Action'ni to'g'ridan chaqirsa), Server Action'ning requireAdmin ushlaydi. Eng keng xato — faqat bir qatlamga ishonish (masalan faqat middleware — yoki faqat UI yashirish) — har qatlam zarur (UI — UX, middleware — markaziy, Server Action — haqiqiy himoya). Bu — production autentifikatsiya tizimining to'liq arxitekturasi: Auth.js (sozlama, provayder, sessiya — murakkab ishni qiladi) + har qatlamda tekshiruv (siz qo'shasiz — UI, middleware, sahifa, Server Action). Autentifikatsiya (kim) + avtorizatsiya (nima qila oladi) + chuqur himoya (har qatlam) = xavfsiz foydalanuvchi tizimi. Har real ilova (e-commerce, ijtimoiy tarmoq, SaaS) shunday quriladi. 14-QISM (Xavfsizlik)da bu yondashyv (defense in depth) chuqurroq ochiladi.


5. To'g'ri va noto'g'ri holatlar

1) Parol

text
 ochiq parol DB'da (DB sizsa — falokat — 2.10)
 bcrypt.hash (hash saqlanadi — Misol 7)

2) Avtorizatsiya joyi

text
 faqat UI yashirish (Server Action ochiq — 2.9)
 har qatlamda tekshir (sahifa + Server Action/API — Misol 5, 10)

3) Sessiyani olish

text
 client useSession sahifa himoyasiga (kontent yuboriladi)
 server auth() (himoya — xavfsiz, tez — Misol 3)

4) Rol

text
 rol sessiyada yo'q (callbacksiz — avtorizatsiya ishlamaydi)
 callbacks (jwt + session — rol — Misol 8)

5) Secret

text
 AUTH_SECRET kodda yoki zaif (token buziladi)
 env'da kuchli (npx auth secret — 2.10)

6) OAuth vs Credentials

text
 Credentials'ni bcrypt'siz (ochiq parol — xavfli)
 OAuth afzal (parol siz saqlamaysiz) yoki Credentials + bcrypt (2.3)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — session is null (kirgan bo'lsa ham)

Sababi: SessionProvider yo'q (client) yoki auth() noto'g'ri import 2.5-bob. Yechimi: server auth(); client SessionProvider (Misol 6).

Xato 2 — session.user.role undefined

Sababi: callbacks yo'q (rol sessiyada emas — 2.6). Yechimi: jwt + session callbacks (Misol 8).

Xato 3 — Oddiy foydalanuvchi admin amalini bajardi

Sababi: Server Action rol tekshirmaydi (faqat UI yashirilgan — 2.9). Yechimi: requireAdmin har Server Action'da (Misol 5).

Xato 4 — OAuth redirect xatosi (callback URL)

Sababi: provayder console'da callback URL noto'g'ri. Yechimi: https://sayt.com/api/auth/callback/google (provayder sozlamasida).

Xato 5 — AUTH_SECRET xatosi

Sababi: secret yo'q yoki noto'g'ri 2.10-bob. Yechimi: .envda AUTH_SECRET (npx auth secret).

Xato 6 — Parol tekshiruvi har doim false

Sababi: ochiq parol vs hash solishtirildi 2.10-bob. Yechimi: bcrypt.compare (hash bilan — Misol 2).

Xato 7 — Middleware sessiyani ko'rmaydi

Sababi: auth middleware noto'g'ri 2.9-bob. Yechimi: export default auth((req) => ...) (req.auth — Misol 4).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Middleware 13.6-bob: auth middleware — markaziy himoya (Misol 4).
  • Server Actions 13.5-bob: Server Action avtorizatsiya (rol — Misol 5).
  • Server Components 13.3-bob: auth() — server'da sessiya (Misol 3).
  • Routing 13.2-bob: redirect — himoyalangan sahifa.
  • DB/Prisma 6.12-bob: adapter — foydalanuví/sessiya DB'da.
  • Forms (13.5, 11.10): login/register forma + validatsiya (Zod).
  • Xavfsizlik (14): parol hash, CSRF, avtorizatsiya (chuqur).
  • Context 12.1-bob: SessionProvider — client sessiya.

8. Eng yaxshi amaliyotlar (best practices)

  • OAuth afzal (parol siz saqlamaysiz — xavfsizroq — 2.3).
  • Parol hash (bcrypt) (ochiq emas — 2.10, Misol 7).
  • Avtorizatsiya har qatlamda (middleware + sahifa + Server Action — 2.9, Misol 10).
  • Server auth() afzal (xavfsiz, tez — Misol 3); client useSession interaktiv.
  • Rol callbacks orqali (sessiyaga — 2.6, Misol 8).
  • AUTH_SECRET kuchli (env'da — 2.10).
  • HTTPS production (cookie/token himoya — 2.10).
  • Validatsiya (Zod) (login/register — Misol 2, 7).
  • Sessiya muddati (maxAge — cheksiz emas — 2.10).
  • Defense in depth (har qatlam mustaqil himoya — Misol 10).

9. Amaliy loyiha: "To'liq Auth Tizimi"

Auth.js bilan to'liq foydalanuvchi tizimini mustahkamlash.

Maqsad

Ilova: ro'yxat/login (OAuth + email/parol), himoyalangan sahifalar, admin panel, rollar.

Talablar (requirements)

  1. Sozlash: Auth.js + Prisma adapter + Google + Credentials (Misol 1, 2).
  2. Ro'yxat: email/parol + bcrypt hash (Misol 7).
  3. Login: OAuth + email/parol sahifasi (Misol 9).
  4. Sessiya: server auth() + client useSession (Misol 3, 6).
  5. Rollar: callbacks (sessiyaga rol — Misol 8).
  6. Sahifa himoya: dashboard (kirgan) — Misol 3.
  7. Middleware: markaziy himoya (dashboard/admin — Misol 4).
  8. Server Action: admin amallar (avtorizatsiya — Misol 5).
  9. Navbar: kirgan/kirmagan UI (Misol 6).
  10. Xavfsizlik: secret, HTTPS, defense in depth (Misol 10).

Maslahatlar (hint)

  • Parol bcrypt (ochiq emas — Xato 6).
  • Rol callbacks (Xato 2).
  • Avtorizatsiya har qatlamda (faqat UI emas — Xato 3).
  • Server auth() himoyaga (Misol 3).
  • AUTH_SECRET env (Xato 5).
  • OAuth callback URL to'g'ri (Xato 4).

"Tayyor" mezonlari (acceptance criteria)

  • OAuth + Credentials login.
  • Ro'yxat (bcrypt hash).
  • Server + client sessiya.
  • Rollar (callbacks).
  • Sahifa himoya (server).
  • Middleware (markaziy).
  • Server Action avtorizatsiya.
  • Defense in depth (har qatlam).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda Next.js'da autentifikatsiyani (Auth.js) chuqur o'rgandik:

  • Authn vs authz 2.1-bob; Auth.js sozlash 2.2-bob; provayderlar (OAuth/Credentials — 2.3); sessiya strategiya (JWT/database — 2.4); sessiyani olish (server/client — 2.5).
  • Callbacklar (rol, signIn — 2.6); adapter 2.7-bob; kirish/chiqish 2.8-bob; avtorizatsiya (rollar — 2.9); xavfsizlik (bcrypt/CSRF — 2.10).
  • Boshqa provayderlar (magic link/passkey/Discord — 2.11); DB sxema, env, token refresh 2.12-bob; Next.js vs backend auth (NestJS — 2.13); muqobillar (Clerk/Lucia/Supabase — 2.14).

Endi siz Next.js'da to'liq foydalanuvchi tizimini qura olasiz: login/ro'yxat (OAuth + email/parol), himoyalangan sahifalar, rollar, va chuqur himoya (har qatlam). Bu — har real ilovaning asosiy qismi.

Keyingi bob — 13.10-bob: Deploy va Production. Auth'ni bildik; endi ilovani dunyoga chiqarishni ko'ramiz: Vercel'da deploy (eng oson — Next.js yaratuvchilari), environment variables (secret production'da), build optimizatsiya, boshqa platformalar (Docker, o'z server — 10-QISM bilan), domen va HTTPS, monitoring va xatolar (logging, analytics), va production checklist. Bu — loyihangizni real foydalanuvchilarga yetkazishning yakuniy qadami.


Foydalanilgan rasmiy/ishonchli manbalar

  • Auth.js rasmiy hujjati — "Getting Started", "Providers" (OAuth, Credentials, Email/magic link, WebAuthn/passkey), "Session strategies" (JWT vs database), "Callbacks" (jwt, session, signIn), "Adapters", "Pages" (custom signIn/error), "TypeScript" (module augmentation)
  • Next.js rasmiy hujjati — "Authentication" bo'limi, middleware bilan auth, Server Components'da sessiya
  • OWASP — Authentication Cheat Sheet va Session Management Cheat Sheet (parol hash, sessiya xavfsizligi); bcrypt/argon2 hujjatlari
  • Prisma adapter va standart DB sxemasi (User/Account/Session/VerificationToken); Drizzle adapter
  • OAuth 2.0 / OpenID Connect spetsifikatsiyasi; provayder developer console'lari (Google, GitHub, Discord — clientId/secret, callback URL)
  • Muqobil yechimlar hujjatlari: Clerk, Lucia, Supabase Auth (taqqoslash uchun)

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
13.9-bob: Autentifikatsiya (NextAuth / Auth.js) — Wisar