Auth arxitekturasi: session, JWT va refresh rotation — amaliy tanlov
So'nggi to'rt yilda men ikkita jiddiy loyihada auth qurdim: maktab boshqaruv tizimi (minglab o'quvchi, ota-ona va o'qituvchi) va yetkazib berish servisi (kuryer mobil ilovasi + dispetcher paneli). Ikkalasida ham birinchi arxitektura yig'ilishida bir xil savol chiqdi: "Session ishlatamizmi yoki JWT?" Va ikkalasida ham to'g'ri javob "vaziyatga qarab" bo'ldi. Bu maqolada uchta asosiy yondashuvni — server session, JWT va access+refresh rotation — real tajriba nuqtai nazaridan taqqoslayman, oxirida esa qaysi holatda nimani tanlash bo'yicha aniq tavsiyalar beraman.
Server session: eski, lekin o'lmagan usul
Klassik sxema juda oddiy. Foydalanuvchi login qiladi, server session yozuvini yaratadi (Redis yoki Postgres'da), uning ID'sini httpOnly cookie'ga joylaydi. Keyingi har bir so'rovda brauzer cookie'ni avtomatik yuboradi, server esa store'dan sessionni topib, foydalanuvchini aniqlaydi.
Kuchli tomonlari:
- Bekor qilish bir qator kod. Foydalanuvchi logout qildi yoki parolini almashtirdi — store'dan yozuvni o'chirasiz, tamom. Maktab tizimida bu bizga juda qo'l keldi: direktor "bu hodimning kirishini hoziroq yoping" desa, bir soniyada yopiladi.
- Server to'liq nazoratda. Nechta faol session bor, qaysi qurilmadan kirilgan — hammasi ko'rinib turadi.
- Brauzer cookie'ni o'zi boshqaradi, frontend'da token bilan o'ynashish shart emas.
Zaif tomonlari:
- Har bir so'rovda store'ga murojaat — Redis bilan bu mikrosekundlar, lekin baribir qo'shimcha bog'liqlik.
- Bir nechta server bo'lsa, umumiy session store kerak (sticky session'ga tayanish — o'zini aldash).
- Mobil ilovalar cookie bilan ishlashi mumkin, lekin bu tabiiy emas — mobil dunyoda token odatiyroq.
JWT: stateless va'dasi va real muammolari
JWT'ning jozibasi bitta jumlaga sig'adi: server hech narsa saqlamaydi, tokendagi imzoni tekshiradi va foydalanuvchini taniydi. Chiroyli. Lekin amalda uchta jiddiy muammo bor.
Birinchisi — bekor qilib bo'lmaslik. JWT muddati tugaguncha amal qiladi. Foydalanuvchi logout qildi? Token hali ham yaroqli. Parol o'g'irlandi va almashtirdik? Eski token yashayveradi. "Blacklist qilamiz" deysizmi — endi har so'rovda ro'yxatni tekshirasiz, ya'ni yana state paydo bo'ldi va stateless afzalligingiz bug'lanib ketdi.
Ikkinchisi — token o'g'irlanishi. XSS orqali yoki log'larga tushib qolgan token muddati tugaguncha to'la huquqli kalit. Muddatni 7 kun qilib qo'ygan bo'lsangiz, hujumchi 7 kun sizning API'ingizda erkin yuradi.
Uchinchisi — hajm. JWT'ga rollar, ruxsatlar, profil ma'lumotlarini tiqishni boshlasangiz, token 2-3 KB bo'lib ketadi va har bir so'rov header'ida yuriladi. Yetkazib berish servisida biz shunday xatoga yo'l qo'ygan edik — kuryer ilovasi sekin internetda har so'rovga keraksiz kilobaytlar tashirdi.
"JWT hamma joyda kerak" degan mif
Ochig'ini aytaman: oddiy monolit + bitta frontend kombinatsiyasida JWT ko'pincha ortiqcha murakkablik. Uni tanlashning asosli sabablari aslida ikkita:
- Mikroservislar. Bitta auth servis token imzolaydi, qolgan o'nlab servis umumiy store'ga bormasdan, faqat ochiq kalit bilan imzoni tekshiradi. Mana bu yerda stateless haqiqatan qimmatli.
- Uchinchi tomon API. Tashqi hamkorlar sizning API'ingizga kirsa (OAuth 2.0 / OpenID Connect oqimlari), standart token formati kerak — bu JWT'ning tabiiy maydoni.
Agar sizda Next.js frontend va bitta Node.js backend bo'lsa, "hamma JWT ishlatyapti-ku" degan argument arxitektura qarori emas. Men shogirdlarimga doim shuni aytaman: texnologiyani muammodan kelib chiqib tanlang, trenddan emas.
Access + refresh juftligi va rotation
Zamonaviy o'rta yo'l — qisqa umrli access token va uzun umrli refresh token juftligi. Oqim so'z bilan quyidagicha:
- Foydalanuvchi login qiladi. Server 10-15 daqiqalik access token va 30 kunlik refresh token beradi.
- Klient API so'rovlarida access token ishlatadi. Server faqat imzoni tekshiradi — store'ga bormaydi.
- Access token eskirganda klient
/auth/refreshendpoint'iga refresh token yuboradi. - Server refresh tokenni bazadan topadi, eskisini darhol bekor qiladi va yangi juftlik qaytaradi. Bu — rotation: har bir refresh token faqat bir marta ishlaydi.
- Bekor qilingan (allaqachon ishlatilgan) refresh token yana kelsa — bu reuse detection signali: token o'g'irlangan. Server shu foydalanuvchining butun token oilasini (family) bekor qiladi va qayta login talab qiladi.
Reuse detection nima uchun bunchalik muhim? Chunki rotation'siz o'g'irlangan refresh token 30 kun jimgina ishlayveradi va siz buni hech qachon bilmaysiz. Rotation bilan esa o'g'ri va haqiqiy foydalanuvchidan qaysi biri eski tokenni ikkinchi bo'lib ishlatsa, tizim darhol sezadi va ikkalasini ham chiqarib yuboradi. Yomon stsenariyda foydalanuvchi bir marta qayta login qiladi — bu o'g'irlangan akkauntdan ancha arzon.
Tokenni qayerda saqlash: localStorage yoki httpOnly cookie?
SPA'larda eng ko'p uchraydigan xato — tokenni localStorage'ga qo'yish. Muammo shundaki, localStorage'ni sahifadagi istalgan JavaScript o'qiy oladi. Bitta XSS zaiflik — kimdir kommentga script tiqib yubordi, yoki npm'dagi bog'liqlik zaharlangan bo'ldi — va token hujumchining serverida.
httpOnly cookie'ni esa JavaScript umuman o'qiy olmaydi, XSS orqali tokenni to'g'ridan-to'g'ri o'g'irlab bo'lmaydi. Evaziga CSRF xavfi keladi: brauzer cookie'ni har so'rovga avtomatik qo'shadi, demak yot saytdagi forma sizning API'ingizga foydalanuvchi nomidan so'rov yuborishi mumkin. Yaxshi yangilik — bu muammo bugun deyarli hal qilingan: SameSite=Lax yoki Strict atributi cross-site so'rovlarda cookie yuborilishini to'sadi.
Mening amaliy tavsiyam: access va refresh tokenlarni `httpOnly` + `Secure` + `SameSite=Strict` cookie'da saqlang, refresh cookie'ning path'ini faqat /auth/refresh'ga cheklang. XSS'dan token o'g'irlash yopiladi, CSRF'dan SameSite himoya qiladi.
Kod: refresh rotation skeleti Express'da
Quyida rotation va reuse detection mantiqining soddalashtirilgan, lekin ishlaydigan skeleti. Bazada refresh tokenning o'zi emas, faqat hash'i saqlanadi — baza sizib chiqsa ham tokenlar ishlatib bo'lmaydigan bo'ladi:
import crypto from "node:crypto";
import express from "express";
import cookieParser from "cookie-parser";
const app = express();
app.use(cookieParser());
const sha256 = (v) => crypto.createHash("sha256").update(v).digest("hex");
async function issueRefreshToken(userId, familyId) {
const raw = crypto.randomBytes(32).toString("base64url");
await db.refreshTokens.create({
hash: sha256(raw),
userId,
familyId,
expiresAt: new Date(Date.now() + 30 * 24 * 3600 * 1000),
});
return raw;
}
app.post("/auth/refresh", async (req, res) => {
const raw = req.cookies.refresh_token;
if (!raw) return res.status(401).json({ error: "no_token" });
const record = await db.refreshTokens.findByHash(sha256(raw));
// Reuse detection: token bekor qilingan bo'lsa ham qayta keldi —
// demak o'g'irlangan, butun oilani yopamiz
if (!record || record.revokedAt) {
if (record) await db.refreshTokens.revokeFamily(record.familyId);
return res.status(401).json({ error: "reuse_detected" });
}
if (record.expiresAt < new Date()) {
return res.status(401).json({ error: "expired" });
}
// Rotation: eskisini bekor qilib, yangisini beramiz
await db.refreshTokens.revoke(record.id);
const newRaw = await issueRefreshToken(record.userId, record.familyId);
res.cookie("refresh_token", newRaw, {
httpOnly: true,
secure: true,
sameSite: "strict",
path: "/auth/refresh",
});
res.json({ accessToken: signAccessToken(record.userId) }); // 15 daqiqa
});Production'da bunga rate limiting, findByHash + revoke'ni bitta tranzaksiyaga o'rash va audit log qo'shiladi, lekin yadro mantiq — shu.
Amaliy tavsiyalar: qaysi holatda nima?
- Oddiy web-app (monolit, server-side render, admin panel): server session + Redis. Eng sodda, eng nazorat qilinadigan variant. Maktab tizimimizning admin qismi aynan shunda ishlaydi va bironta muammo bermagan.
- SPA + API (React/Vue frontend, alohida backend): access + refresh juftligi, ikkalasi ham
httpOnlycookie'da,SameSite=Strictbilan. localStorage'ga token qo'ymang. - Mobil ilova: refresh rotation majburiy. Tokenlarni platformaning secure storage'ida (Keychain / Keystore) saqlang. Yetkazib berish servisida kuryer ilovasi aynan shu sxemada — 15 daqiqalik access, 30 kunlik rotating refresh.
- Mikroservislar yoki tashqi hamkor API: mana endi JWT o'z o'rnida — qisqa muddatli access sifatida, ichki servislar imzoni mustaqil tekshiradi.
Xulosa
Auth arxitekturasida "eng zo'r" yechim yo'q, kontekst bor. Session — soddalik va nazorat, JWT — servislar orasida ishonchni tashish vositasi, refresh rotation esa ikkalasining o'rtasidagi amaliy muvozanat. Eng muhim ikki qoida: tokenni JavaScript o'qiy oladigan joyda saqlamang va refresh token ishlatsangiz, rotation'siz ishlatmang. Qolgani — loyihangiz miqyosiga bog'liq muhandislik qarori.