IlmHamroh
Dasturlash kitobi (eski nashr)/5-QISM — Nodejs16/23-dars18 daqiqa
Mundarija (34)

Access va refresh token — JWT rotation va xavfsiz saqlash (5.16-bob)

5-QISM — Node.js Backend · 16-mavzu


1. Kirish va motivatsiya

5.15-bobda JWT bilan autentifikatsiyani o'rgandik va oxirida uning katta kamchiligini ko'rdik: JWT stateless — server uni eslamaydi, shuning uchun bekor qilib bo'lmaydi. Token o'g'irlansa yoki foydalanuvchi logout qilsa ham, token muddati tugaguncha amal qiladi. Bu — jiddiy xavfsizlik muammosi (14).

Yechim oddiy ko'rinadi: tokenni qisqa muddatli qilamiz (masalan 15 daqiqa) — o'g'irlansa, tez yaroqsiz bo'ladi. Lekin shunda boshqa muammo: foydalanuvchi har 15 daqiqada qayta login qilishi kerakmi? Yo'q — bu dahshatli UX. Bizga ikkalasi ham kerak: xavfsizlik (qisqa muddat) va qulaylik (uzoq "kirgan holda qolish").

Yechim — ikki token strategiyasi: qisqa muddatli access token (har so'rovda ishlatiladi) + uzoq muddatli refresh token (faqat yangi access token olish uchun). Bu — zamonaviy auth'ning standart yechimi (RFC 9700, 2026'da majburiy amaliyot). Bu bobda uni professional, xavfsiz darajada quramiz: token rotatsiyasi, qayta ishlatishni aniqlash (reuse detection), xavfsiz saqlash va bekor qilish.

O'xshatish: access token — mehmonxona xona kaliti (vaqtinchalik, tez muddati tugaydi, yo'qotsangiz katta zarar yo'q). Refresh token — pasport (qaltis, seyfda saqlanadi, faqat resepshnda yangi kalit olish uchun ko'rsatiladi). Har eshikda pasport emas, kalit ishlatasiz (access). Kalit muddati tugasa — resepshnga borib (refresh), pasport bilan yangi kalit olasiz. Pasport o'g'irlansa — uni bekor qilib (revoke), yangisini berishadi.

Nega muhim?

  • 5.15 muammosini hal qiladi — qisqa access (xavfsiz) + uzoq refresh (qulay).
  • Zamonaviy standart (14) — RFC 9700: rotatsiya majburiy; har real ilovada bor.
  • O'g'irlikni aniqlash — reuse detection token o'g'irligini fosh qiladi.
  • Intervyu/ish — "access va refresh token farqi" — klassik savol.

2. Nazariya — chuqur tushuntirish

2.1. Bitta token muammosi (takrorlash — 5.15)

Bitta JWT bilan ikki yomon variant bor:

text
  Uzoq muddatli token (masalan 30 kun):
    ✓ Qulay (kam login)
    ✗ O'g'irlansa — 30 kun hacker kirib turadi (bekor qila olmaymiz! — 14)

  Qisqa muddatli token (masalan 15 daqiqa):
    ✓ Xavfsiz (o'g'irlansa, tez tugaydi)
    ✗ Har 15 daqiqada qayta login (dahshatli UX)

Ikkalasi ham yomon. Yechim — ikkisini birlashtirish: qisqa access + uzoq refresh 2.2-bob.

2.2. Ikki token g'oyasi (access + refresh)

text
  ACCESS TOKEN — qisqa muddatli (15-60 daqiqa)
    → HAR so'rovda ishlatiladi (himoyalangan endpoint)
    → o'g'irlansa, tez yaroqsiz (xavf kam)
    → stateless JWT (server eslamaydi — tez tekshiriladi)

  REFRESH TOKEN — uzoq muddatli (7-90 kun)
    → FAQAT yangi access token olish uchun (/refresh endpoint)
    → DB'da saqlanadi (bekor qilinishi mumkin — stateful!)
    → kamdan-kam ishlatiladi (har 15 daqiqada bir marta)

Asosiy g'oya (descope/auth0): access token — tez-tez ishlatiladigan, lekin arzon (o'g'irlansa tez tugaydi). Refresh token — kam ishlatiladigan, lekin qimmat (uzoq), shuning uchun DB'da nazorat ostida (bekor qilinadi). Xavfsizlik (qisqa access) + qulaylik (uzoq refresh) — ikkalasi ham.

Access token payload'ida nima saqlanadi: faqat minimal, maxfiy bo'lmagan ma'lumot — sub (foydalanuvchi ID), rol (RBAC uchun — 5.17), kerak bo'lsa email. Access token stateless — har so'rovda DB'siz o'qiladi, shuning uchun ruxsatni tekshirish uchun zarur minimal ma'lumot bo'lsa kifoya. Hech qachon parol, parol hash, to'liq sezgir profil yoki maxfiy ma'lumot qo'shmang — JWT payload faqat imzolangan, shifrlanmagan (Base64, hamma o'qiy oladi — 5.15). Payload qancha kichik bo'lsa, token shuncha yengil va har so'rovda tez uzatiladi. Refresh token payload'i esa undan ham minimal — odatda faqat sub (2.2, Misol 1).

2.3. Token oqimi (umumiy rasm)

text
  LOGIN:
  email+parol → server: access (15min) + refresh (7kun) beradi
    access  → xotirada/cookie
    refresh → httpOnly cookie + DB'ga saqlanadi 2.7-bob

  HAR SO'ROV:
  access token bilan → himoyalangan endpoint → ✓

  ACCESS MUDDATI TUGADI (15min o'tdi):
  so'rov → 401 (access eskirgan) →
  client /refresh ga refresh token yuboradi →
  server refresh'ni DB'da tekshiradi → YANGI access (+ yangi refresh — 2.4) →
  client yangi access bilan davom etadi (foydalanuvchi sezmaydi!)

  LOGOUT:
  refresh token'ni DB'dan o'chir (bekor — 2.6)
text
  ┌──────┐  login   ┌────────┐
  │Client│─────────▶│ Server │  access(15m) + refresh(7d)
  │      │◀─────────│        │
  └──────┘          └────────┘
     │ access bilan so'rovlar (15 daqiqa)
     │ ...access tugadi → 401...
     │ refresh bilan /refresh
     ▼
  yangi access (+ yangi refresh) → davom

2.4. Refresh token rotation (rotatsiya) — muhim

Token rotation — har refresh ishlatilganda, yangi refresh token ham beriladi, eskisi bekor qilinadi (authjs/okta, RFC 9700):

text
  Refresh R1 ishlatildi → yangi access + yangi refresh R2
    → R1 BEKOR qilinadi (endi ishlamaydi)
  Keyingi safar R2 ishlatiladi → R3, R2 bekor
  ...

Nega rotatsiya (14): refresh token uzoq muddatli — o'g'irlansa xavfli. Rotatsiya bilan har refresh bir martalik bo'ladi. Bu — reuse detectionga 2.5-bob imkon beradi: eski (allaqachon ishlatilgan) refresh qayta ishlatilsa — o'g'irlik signali. 2026'da rotatsiya majburiy (RFC 9700).

2.5. Reuse detection (qayta ishlatishni aniqlash) — o'g'irlikni fosh qilish

Eng kuchli xavfsizlik mexanizmi: agar allaqachon ishlatilgan (bekor qilingan) refresh token qayta ishlatilsa — bu o'g'irlik belgisi (descope/auth0):

text
  Normal: R1 → R2 → R3 (har biri bir marta)

  O'G'IRLIK stsenariysi:
  Hacker R1'ni o'g'irladi. Lekin haqiqiy foydalanuvchi ham R1'ni ishlatdi → R2 oldi (R1 bekor)
  Hacker R1'ni ishlatmoqchi → R1 ALLAQACHON BEKOR → REUSE DETECTED!
  → butun token oilasini (R1,R2,R3...) BEKOR QILISH → ikkalasi ham qayta login qilsin (14)

Token family (oila): bir login'dan kelib chiqqan barcha refresh tokenlar — bitta "oila". Reuse aniqlansa, butun oila bekor qilinadi (faqat bitta token emas). Bu — o'g'irlangan tokenni "uzoq muddatli kirish"dan "aniqlanadigan hodisa"ga aylantiradi (manbalar).

2.6. Token bekor qilish (revocation)

Refresh token DB'da saqlangani uchun, uni bekor qilish mumkin (access — stateless, bekor qilib bo'lmaydi, lekin qisqa):

text
  Logout            → o'sha refresh'ni DB'dan o'chirish
  "Barcha qurilmadan chiq" → foydalanuvchining BARCHA refresh'larini o'chirish
  Parol o'zgardi    → barcha refresh'ni bekor qilish (xavfsizlik — 14)
  O'g'irlik aniqlandi → token oilasini bekor qilish (2.5)

Access token bekor qilish: stateless JWT bekor qilinmaydi. Lekin qisqa (15min) — tez tugaydi. Agar darhol bekor kerak bo'lsa — Redis blacklist (bekor qilingan access tokenlar, qisqa muddatga — 5.21).

2.7. Tokenlarni qayerda saqlash (eng xavfsiz — 14)

Eng xavfsiz strategiya (curity/auth.js):

text
  Access token   → XOTIRADA (JS o'zgaruvchi/React state — 11)
                   yoki httpOnly cookie
                   → qisqa muddatli, sahifa yangilashda yo'qoladi (refresh bilan tiklanadi)

  Refresh token  → httpOnly + Secure + SameSite cookie (14, 5.15)
                   → JS o'qiy olmaydi (XSS himoyasi); DB'da nusxasi (bekor uchun)

Refresh token — eng qimmat, eng himoyalangan (14): httpOnly cookie'da (JS ko'rmaydi — XSS himoyasi); localStorageda hech qachon 5.15-bob. DB'da hash qilib saqlash (parol kabi — bcrypt — 2.8) yanada xavfsiz (DB sizsa, tokenlar ochiq bo'lmaydi).

2.8. Refresh token'ni DB'da xavfsiz saqlash

Refresh token DB'da saqlanadi (bekor uchun — 2.6). Lekin uni ochiq saqlash xavfli (DB sizsa). Hash qilib saqlash (parol kabi — 5.15):

text
  refresh_tokens jadvali:
  | id | userId | tokenHash        | family | expiresAt | revoked |
  | 1  | 7      | hash(token)      | fam-1  | +7kun     | false   |

  → tokenning O'ZI emas, HASH'i (DB sizsa, ishlatib bo'lmaydi — 14)
  → family: reuse detection uchun 2.5-bob; revoked: bekor belgisi (2.6)

2.9. Access va refresh muddatlari (qancha)

Tavsiya etilgan muddatlar (guptadeepak/CIAM 2026):

text
  Access token:  15 daqiqa – 1 soat (qisqa — xavfsizlik)
  Refresh token: 7 – 30 kun (web); 30-90 kun (mobil app)
  Sezgir (bank): qisqaroq (access 5-15min)

Balans: qisqaroq access — xavfsizroq, lekin ko'proq refresh so'rovi. 15 daqiqa — ko'p ilova uchun yaxshi balans. Refresh muddatini loyiha (sezgirlik) bo'yicha tanlang.

2.10. Silent refresh (foydalanuvchi sezmaydi)

Yaxshi UX: access token tugaganda, foydalanuvchi sezmasdan yangilanishi kerak. Frontend (11) buni avtomatik qiladi:

text
  So'rov → 401 (access tugagan) →
  frontend interceptor (Axios — 2.18-JS) ushlaydi →
  /refresh ga so'rov (refresh cookie avtomatik) →
  yangi access olinadi →
  asl so'rov YANGI access bilan QAYTA yuboriladi →
  foydalanuvchi hech narsa sezmaydi (uzluksiz)

Bu — frontend mas'uliyati (11, Axios interceptor — 2.18-JS), lekin backend /refresh endpoint'ini to'g'ri berishi kerak. Backend + frontend birga ishlaydi.

2.11. Xavfsizlik amaliyotlari (14)

text
  ✓ Access qisqa (15-60min), refresh uzoq (7-30kun) — 2.9
  ✓ Refresh rotatsiya (har ishlatishda yangi — 2.4); reuse detection 2.5-bob
  ✓ Refresh httpOnly cookie + DB'da hash (2.7, 2.8)
  ✓ Logout/parol o'zgarishida refresh bekor 2.6-bob
  ✓ Access va refresh uchun ALOHIDA secret 5.8-bob
  ✓ Refresh endpoint'iga rate limiting 5.20-bob
  ✓ HTTPS (14); refresh oilasini kuzat (o'g'irlik — 2.5)

3. Sintaksis — tez ma'lumotnoma

js
import jwt from "jsonwebtoken";

// Ikki token yasash (2.2)
const access = jwt.sign({ sub: id, rol }, ACCESS_SECRET, { expiresIn: "15m" });
const refresh = jwt.sign({ sub: id }, REFRESH_SECRET, { expiresIn: "7d" });

// Refresh oqimi (2.3)
// 1. refresh'ni verify   2. DB'da tekshir (bekor emasmi)   3. rotatsiya (yangi refresh)
// 4. yangi access ber

// Saqlash (2.7)
res.cookie("refreshToken", refresh, { httpOnly: true, secure: true, sameSite: "lax", path: "/api/auth/refresh" });

4. Batafsil kod namunalari

Misol 1 — Token yasash funksiyalari (2.2)

js
import jwt from "jsonwebtoken";
import { config } from "../config/index.js";          // (5.8)

// Access — qisqa, asosiy ma'lumot bilan (2.2)
export const accessYasash = (user) =>
  jwt.sign(
    { sub: user.id, rol: user.rol },                   // payload (5.15)
    config.jwt.accessSecret,                           // ALOHIDA secret (2.11)
    { expiresIn: "15m" }                               // qisqa (2.9)
  );

// Refresh — uzoq, minimal ma'lumot (2.2)
export const refreshYasash = (user) =>
  jwt.sign(
    { sub: user.id },                                  // faqat ID
    config.jwt.refreshSecret,                          // boshqa secret (2.11)
    { expiresIn: "7d" }                                // uzoq (2.9)
  );

Misol 2 — Login (ikki token beradi — 2.3)

js
import bcrypt from "bcrypt";

export const login = async (req, res, next) => {
  try {
    const { email, parol } = req.body;
    const user = await User.findOne({ email }).select("+parol");   // (5.15)
    if (!user || !(await bcrypt.compare(parol, user.parol))) {
      return res.status(401).json({ error: "Email yoki parol noto'g'ri" });   // (5.15)
    }
    // Ikki token (2.2)
    const accessToken = accessYasash(user);
    const refreshToken = refreshYasash(user);
    // Refresh'ni DB'da hash qilib saqlash (2.8)
    await RefreshToken.create({
      userId: user.id,
      tokenHash: await bcrypt.hash(refreshToken, 10),  // hash (DB sizsa — 14)
      family: crypto.randomUUID(),                     // oila (2.5)
      expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000),
    });
    // Refresh — httpOnly cookie (2.7)
    res.cookie("refreshToken", refreshToken, {
      httpOnly: true, secure: config.isProd, sameSite: "lax",
      path: "/api/auth",                               // faqat auth route'larida (2.7)
      maxAge: 7 * 24 * 3600 * 1000,
    });
    // Access — javobda (frontend xotirada saqlaydi — 2.7)
    res.json({ accessToken, user: { id: user.id, email: user.email, rol: user.rol } });
  } catch (err) { next(err); }
};

Misol 3 — Refresh endpoint (yangi access — 2.3, 2.4)

js
import jwt from "jsonwebtoken";
import bcrypt from "bcrypt";

export const refresh = async (req, res, next) => {
  try {
    const token = req.cookies.refreshToken;            // cookie'dan (2.7)
    if (!token) return res.status(401).json({ error: "Refresh token yo'q" });

    // 1. Imzoni tekshirish (2.2)
    let payload;
    try {
      payload = jwt.verify(token, config.jwt.refreshSecret);
    } catch {
      return res.status(401).json({ error: "Refresh noto'g'ri/tugagan" });
    }

    // 2. DB'da topish va tekshirish (bekor emasmi — 2.6)
    const saqlangan = await RefreshToken.findOne({ userId: payload.sub, revoked: false });
    if (!saqlangan || !(await bcrypt.compare(token, saqlangan.tokenHash))) {
      // REUSE DETECTION: bekor qilingan token qayta ishlatildi → o'g'irlik 2.5-bob!
      await RefreshToken.updateMany({ userId: payload.sub }, { revoked: true });   // butun oila (2.5)
      return res.status(401).json({ error: "Xavfsizlik buzilishi — qayta kiring" });
    }

    // 3. ROTATSIYA: eskini bekor qilish, yangi refresh berish (2.4)
    saqlangan.revoked = true;
    await saqlangan.save();
    const user = await User.findById(payload.sub);
    const yangiRefresh = refreshYasash(user);
    await RefreshToken.create({
      userId: user.id, tokenHash: await bcrypt.hash(yangiRefresh, 10),
      family: saqlangan.family, expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000),
    });
    res.cookie("refreshToken", yangiRefresh, {
      httpOnly: true, secure: config.isProd, sameSite: "lax", path: "/api/auth",
      maxAge: 7 * 24 * 3600 * 1000,
    });

    // 4. Yangi access berish (2.3)
    res.json({ accessToken: accessYasash(user) });
  } catch (err) { next(err); }
};

Misol 4 — Logout (refresh bekor — 2.6)

js
export const logout = async (req, res, next) => {
  try {
    const token = req.cookies.refreshToken;
    if (token) {
      const payload = jwt.decode(token);               // (5.15)
      // DB'dan o'chir/bekor qil (2.6)
      await RefreshToken.updateMany({ userId: payload?.sub }, { revoked: true });
    }
    res.clearCookie("refreshToken", { path: "/api/auth" });   // cookie tozalash (5.15)
    res.json({ message: "Tizimdan chiqdingiz" });
  } catch (err) { next(err); }
};

Misol 5 — Access token middleware (himoya — 5.15)

js
import jwt from "jsonwebtoken";

export const auth = (req, res, next) => {
  const token = req.headers.authorization?.startsWith("Bearer ")
    ? req.headers.authorization.slice(7)               // header'dan (access — 2.7)
    : null;
  if (!token) return res.status(401).json({ error: "Avtorizatsiya kerak" });
  try {
    const payload = jwt.verify(token, config.jwt.accessSecret);   // access secret (2.11)
    req.user = { id: payload.sub, rol: payload.rol };
    next();
  } catch (err) {
    // Muddat tugagan bo'lsa — maxsus kod (frontend refresh qilsin — 2.10)
    if (err.name === "TokenExpiredError") {
      return res.status(401).json({ error: "Access tugagan", code: "TOKEN_EXPIRED" });
    }
    return res.status(401).json({ error: "Token noto'g'ri" });
  }
};

Misol 6 — "Barcha qurilmadan chiqish" (2.6)

js
export const hammadanChiq = async (req, res, next) => {
  try {
    // Foydalanuvchining BARCHA refresh tokenlarini bekor qilish (2.6)
    await RefreshToken.updateMany({ userId: req.user.id }, { revoked: true });
    res.clearCookie("refreshToken", { path: "/api/auth" });
    res.json({ message: "Barcha qurilmalardan chiqildi" });
    // Endi hech bir qurilmada refresh ishlamaydi (access'lar 15min ichida tugaydi)
  } catch (err) { next(err); }
};

Misol 7 — Frontend silent refresh (Axios interceptor — 2.10)

js
// frontend (11, 2.18-JS) — backend uchun kontekst
import axios from "axios";
const api = axios.create({ baseURL: "/api", withCredentials: true });   // cookie uchun

// Har so'rovga access token qo'sh (xotiradan)
api.interceptors.request.use((cfg) => {
  if (accessToken) cfg.headers.Authorization = `Bearer ${accessToken}`;
  return cfg;
});

// 401 (access tugagan) bo'lsa — avtomatik refresh + qayta urinish (2.10)
api.interceptors.response.use(null, async (error) => {
  if (error.response?.status === 401 && error.response.data.code === "TOKEN_EXPIRED") {
    const { data } = await api.post("/auth/refresh");   // refresh cookie avtomatik
    accessToken = data.accessToken;                      // yangi access (xotirada)
    error.config.headers.Authorization = `Bearer ${accessToken}`;
    return api(error.config);                            // asl so'rovni qayta yuborish
  }
  return Promise.reject(error);
});

Misol 8 — Parol o'zgarganda barcha tokenni bekor (2.6, 14)

js
export const parolOzgartir = async (req, res, next) => {
  try {
    const user = await User.findById(req.user.id).select("+parol");
    if (!(await bcrypt.compare(req.body.eskiParol, user.parol))) {
      return res.status(401).json({ error: "Eski parol noto'g'ri" });
    }
    user.parol = await bcrypt.hash(req.body.yangiParol, 12);   // (5.15)
    await user.save();
    // Xavfsizlik: barcha refresh'ni bekor qilish (boshqa qurilmalar chiqsin — 2.6, 14)
    await RefreshToken.updateMany({ userId: user.id }, { revoked: true });
    res.clearCookie("refreshToken", { path: "/api/auth" });
    res.json({ message: "Parol o'zgartirildi. Qaytadan kiring." });
  } catch (err) { next(err); }
};

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

1) Faqat bitta uzoq muddatli token

text
❌ bitta 30 kunlik token → o'g'irlansa bekor qila olmaysiz (2.1, 14)
✅ qisqa access + uzoq refresh (DB'da, bekor qilinadi)

2) Refresh token'ni localStorage'da

text
❌ localStorage → XSS o'g'irlaydi (refresh — eng qimmat! — 2.7, 14)
✅ httpOnly cookie + DB'da hash

3) Rotatsiyasiz refresh

text
❌ bir refresh qayta-qayta ishlatiladi → reuse detection imkonsiz 2.4-bob
✅ har refresh'da yangi (rotatsiya) + eski bekor

4) Refresh'ni DB'da ochiq saqlash

js
// ❌ DB sizsa — tokenlar ochiq (14, 2.8)
await RefreshToken.create({ token: refreshToken });

// ✅ hash
await RefreshToken.create({ tokenHash: await bcrypt.hash(refreshToken, 10) });

5) Access va refresh uchun bitta secret

js
// ❌ bitta secret (2.11)
jwt.sign(p, SECRET); // ikkalasi ham

// ✅ alohida (access secret sizsa, refresh xavfsiz)
jwt.sign(p, ACCESS_SECRET); jwt.sign(p, REFRESH_SECRET);

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Refresh ishlamaydi (cookie kelmaydi)

Sababi: cookie path mos emas, yoki frontend withCredentials/CORS credentials yo'q 5.20-bob. Yechimi: path to'g'ri (/api/auth); withCredentials: true; CORS credentials: true.

Xato 2 — Har 15 daqiqada logout bo'lib qoladi

Sababi: refresh oqimi yo'q yoki frontend 401'da refresh qilmaydi 2.10-bob. Yechimi: Axios interceptor (Misol 7); /refresh endpoint.

Xato 3 — Reuse detection xato ishlaydi (normal foydalanuvchi chiqib ketadi)

Sababi: parallel so'rovlar (ikki tab) bir refresh'ni bir vaqtda ishlatdi. Yechimi: qisqa "grace period"; yoki bitta refresh so'rovini navbatlash (frontend); family logikasini ehtiyotkorlik bilan qo'llash.

Xato 4 — Logout'dan keyin ham access ishlaydi

Sababi: access stateless — bekor qilib bo'lmaydi 2.6-bob. Yechimi: bu normal (access 15min'da tugaydi); darhol kerak bo'lsa — Redis blacklist 5.21-bob.

Xato 5 — TokenExpiredError access uchun

Sababi: access muddati tugagan (kutilgan — 2.9). Yechimi: frontend refresh qilsin (code: TOKEN_EXPIRED — Misol 5, 7).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Autentifikatsiya 5.15-bob: bu uni davom ettiradi (JWT, bcrypt, cookie).
  • Env 5.8-bob: access/refresh alohida secret.
  • Error handling 5.10-bob: 401, TOKEN_EXPIRED.
  • RBAC 5.17-bob: access payload'dagi rol.
  • Rate limiting 5.20-bob: refresh/login himoyasi.
  • Redis 5.21-bob: access blacklist; refresh saqlash.
  • DB (6): refresh_tokens jadvali (hash, family, revoked).
  • Axios (2.18-JS): frontend interceptor — silent refresh.
  • React (11): access xotirada, refresh cookie.
  • NestJS 8.16-bob: JWT strategy, refresh guard — shu g'oya.

8. Eng yaxshi amaliyotlar (best practices)

  • Qisqa access (15-60min) + uzoq refresh (7-30kun — 2.2, 2.9).
  • Refresh rotatsiya (har ishlatishda yangi — 2.4); reuse detection + family bekor (2.5, 14).
  • Refresh httpOnly cookie + DB'da hash (2.7, 2.8, 14); access xotirada.
  • Alohida secret access va refresh uchun (2.11, 5.8).
  • Logout/parol o'zgarishi — refresh bekor 2.6-bob; "barcha qurilmadan chiq".
  • Silent refresh (frontend interceptor — 2.10) — yaxshi UX.
  • Refresh/login'ga rate limiting (5.20, 14).
  • code: TOKEN_EXPIRED — frontend refresh signali (Misol 5).
  • Darhol bekor kerak bo'lsa — Redis access blacklist (2.6, 5.21).
  • HTTPS (production — 14).

9. Amaliy loyiha: "Access/Refresh Token Tizimi (rotatsiya bilan)"

Professional token strategiyasini mustahkamlash.

Maqsad

Access + refresh token, rotatsiya, reuse detection va xavfsiz saqlash bilan to'liq token tizimini qurish (5.15 auth'ini kengaytirib).

Talablar (requirements)

  1. Login: access (15min) + refresh (7kun); refresh httpOnly cookie + DB'da hash (Misol 2, 2.7, 2.8).
  2. Refresh endpoint: verify → DB tekshir → rotatsiya (yangi refresh, eski bekor) → yangi access (Misol 3, 2.4).
  3. Reuse detection: bekor qilingan refresh ishlatilsa → butun oilani bekor (Misol 3, 2.5).
  4. Logout: refresh bekor + cookie tozalash (Misol 4, 2.6).
  5. Access middleware: verify; TokenExpiredError → code: TOKEN_EXPIRED (Misol 5).
  6. "Barcha qurilmadan chiqish": barcha refresh bekor (Misol 6, 2.6).
  7. Parol o'zgarishida: barcha refresh bekor (Misol 8, 14).
  8. Alohida secret access va refresh uchun (.env — 2.11, 5.8).
  9. (Bonus) Frontend silent refresh: Axios interceptor (Misol 7, 2.10).
  10. (Bonus) Refresh endpoint'iga rate limiting 5.20-bob.

Maslahatlar (hint)

  • Ikki secret: ACCESS_SECRET, REFRESH_SECRET 2.11-bob.
  • Refresh DB: { userId, tokenHash, family, expiresAt, revoked } 2.8-bob.
  • Rotatsiya: eski revoked = true, yangi yarat 2.4-bob.
  • Reuse: topilmagan/bekor refresh → updateMany({ userId }, { revoked: true }) 2.5-bob.
  • Cookie path: "/api/auth" (faqat auth route — 2.7).
  • 401'da code: TOKEN_EXPIRED (frontend refresh — Misol 5).

"Tayyor" mezonlari (acceptance criteria)

  • Login access + refresh beradi (refresh cookie + DB hash).
  • Refresh endpoint yangi access beradi (rotatsiya bilan).
  • Eski refresh ishlamaydi (rotatsiya); reuse → oila bekor.
  • Logout refresh'ni bekor qiladi.
  • Access muddati tugaganda TOKEN_EXPIRED qaytadi.
  • "Barcha qurilmadan chiqish" ishlaydi.
  • Parol o'zgarishida tokenlar bekor.
  • Access va refresh alohida secret.
  • (Bonus) Frontend avtomatik refresh / rate limit.

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


10. Xulosa va keyingi bobga ko'prik

Bu bobda zamonaviy auth'ning standart yechimini — access/refresh token strategiyasini — chuqur o'rgandik:

  • Bitta token muammosi (uzoq — xavfli, qisqa — yomon UX — 2.1) → ikki token (access qisqa + refresh uzoq — 2.2).
  • Oqim 2.3-bob: access har so'rovda; tugasa — refresh bilan yangilanadi (silent — 2.10).
  • Rotatsiya (har refresh'da yangi — 2.4) + reuse detection (o'g'irlikni fosh, oilani bekor — 2.5) — RFC 9700 majburiy.
  • Bekor qilish (logout/parol/o'g'irlik — 2.6); saqlash (refresh httpOnly cookie + DB hash; access xotira — 2.7, 2.8).
  • Muddatlar (access 15-60min, refresh 7-30kun — 2.9); alohida secret; xavfsizlik (2.11, 14).

Keyingi bob — 5.17-bob: Authorization — RBAC (role-based access control). Foydalanuvchini aniqlash (authentication — 5.15, 5.16) tugadi; endi avtorizatsiya — "kimga nimaga ruxsat bor?" — ga o'tamiz. RBAC (rollarga asoslangan ruxsat): user/admin/moderator rollari, ruxsatlarni tekshiruvchi middleware, rol iyerarxiyasi va dinamik ruxsatlar — chuqur o'rganamiz.


Foydalanilgan rasmiy/ishonchli manbalar

  • Auth.js — Refresh Token Rotation; Auth0 — Refresh Tokens; Okta — refresh token rotation
  • Descope — Refresh Token Rotation & reuse detection; RFC 9700 (OAuth 2.0 Security BCP 2025)
  • Curity / CIAM Compass — token storage & lifetime best practices 2026
Ulashish:Telegram'da

O'qiganingizni mustahkamlang

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!