JWT — yaratish, tekshirish, access va refresh token
Muammodan boshlaylik
Foydalanuvchi login qildi. Keyingi so'rovlarda server uni qanday taniydi?
Eng oddiy yechim — har so'rovda login va parol yuborish. Lekin bu xavfli: parol har so'rovda tarmoqda yuriladi, ushlab olinishi mumkin.
Ikkinchi yechim — session. Login qilganda server "session" yaratadi, ID ni cookie ga saqlaydi. Keyingi so'rovlarda cookie yuboriladi, server session ni topadi. Lekin bu ham muammo: server xotirasida session saqlanadi, bir nechta server bo'lsa — har biri o'z sessionini bilmaydi.
JWT — uchinchi yechim. Server hech narsa saqlamaydi. Foydalanuvchiga token beriladi, u har so'rovda o'sha tokenni yuboradi. Server tokenni tekshiradi — ichida kim ekani yozilgan.
JWT nima?
JWT — JSON Web Token. Uch qismdan iborat, nuqta bilan ajratilgan:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9.abc123
↑ ↑ ↑
Header Payload Signature
Header — algoritm haqida ma'lumot.
Payload — asosiy ma'lumot. Foydalanuvchi ID si, roli, token muddati — bularning hammasi shu yerda. Bu qism ochiq — istalgan kishi o'qiy oladi. Shuning uchun parol, kredit karta raqami kabi maxfiy ma'lumotlar yozilmaydi.
Signature — imzo. Header + Payload + maxfiy kalit bilan hisoblangan. Bu imzo tufayli token o'zgartirilganini bilish mumkin. Kimdir payload ni o'zgartirib, o'zini admin qilib ko'rsatmoqchi bo'lsa — imzo mos kelmaydi, server rad etadi.
Hayotiy o'xshatma: pasport. Ichida kim ekani yozilgan (payload), davlat muhri bor (signature). Muhrsiz pasport — soxta. Muhri bor pasport — ishonchli.
npm install jsonwebtoken
Token yaratish
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET; // maxfiy kalit — .env da
function createToken(userId, role) {
return jwt.sign(
{ userId, role }, // payload — tokenga yoziladigan ma'lumot
SECRET,
{ expiresIn: '15m' } // 15 daqiqadan keyin eskiradi
);
}
const token = createToken(1, 'user');
// 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9...'
expiresIn — token muddati. '15m' — 15 daqiqa, '1h' — 1 soat, '7d' — 7 kun.
Token tekshirish
function verifyToken(token) {
try {
const decoded = jwt.verify(token, SECRET);
return decoded; // { userId: 1, role: 'user', iat: ..., exp: ... }
} catch (err) {
return null; // token yaroqsiz yoki eskirgan
}
}
jwt.verify ikki holda xato chiqaradi: token o'zgartirilgan bo'lsa yoki muddati o'tgan bo'lsa. Ikkalasi ham null qaytariladi — foydalanuvchi qayta login qilishi kerak.
Login endpoint
const bcrypt = require('bcrypt');
router.post('/login', async (req, res, next) => {
try {
const { email, password } = req.body;
// Foydalanuvchini topish
const result = await pool.query(
'SELECT * FROM users WHERE email = $1',
[email]
);
const user = result.rows[0];
if (!user) {
return res.status(401).json({ message: 'Email yoki parol noto\'g\'ri' });
}
// Parolni tekshirish
const isValid = await bcrypt.compare(password, user.password_hash);
if (!isValid) {
return res.status(401).json({ message: 'Email yoki parol noto\'g\'ri' });
}
// Token yaratish
const token = jwt.sign(
{ userId: user.id, role: user.role },
SECRET,
{ expiresIn: '15m' }
);
res.json({ token });
} catch (err) {
next(err);
}
});
Nima uchun "Email yoki parol noto'g'ri" — ikkalasi uchun bir xil xabar? Agar "Email topilmadi" deb yozilsa — hacker qaysi emaillar mavjudligini bilib oladi.
Auth middleware
Token har so'rovda Authorization headerida yuboriladi:
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Middleware bu tokenni tekshiradi:
function authenticate(req, res, next) {
const authHeader = req.headers['authorization'];
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Token kerak' });
}
const token = authHeader.split(' ')[1];
const decoded = verifyToken(token);
if (!decoded) {
return res.status(401).json({ message: 'Token yaroqsiz yoki eskirgan' });
}
req.user = decoded; // { userId, role }
next();
}
Access va Refresh token — nima uchun ikki token?
Access token muddati qisqa — 15 daqiqa. Nima uchun? Agar token o'g'irlansa — 15 daqiqadan keyin o'zi eskiradi. Uzoq muddat bo'lsa — o'g'irlangan token uzoq vaqt ishlatilishi mumkin.
Lekin 15 daqiqada bir marta qayta login qilish — foydalanuvchi uchun noqulay.
Yechim — ikki token:
Access token — qisqa muddat (15 daqiqa). Har so'rovda yuboriladi.
Refresh token — uzun muddat (7-30 kun). Faqat access token yangilash uchun ishlatiladi. Xavfsizroq joyda saqlanadi (httpOnly cookie).
Foydalanuvchi login qildi
↓
Access token (15 daqiqa) + Refresh token (7 kun) berildi
↓
Har so'rovda access token yuboriladi
↓
15 daqiqadan keyin access token eskirdi
↓
Refresh token bilan yangi access token so'raladi
↓
Yangi access token berildi (refresh token hali amal qiladi)
router.post('/login', async (req, res, next) => {
try {
// ... foydalanuvchini tekshirish ...
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET, // alohida maxfiy kalit
{ expiresIn: '7d' }
);
// Refresh token — httpOnly cookie da (JavaScript o'qiy olmaydi)
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 kun
});
res.json({ accessToken });
} catch (err) {
next(err);
}
});
// Access token yangilash
router.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) {
return res.status(401).json({ message: 'Refresh token kerak' });
}
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
const accessToken = jwt.sign(
{ userId: decoded.userId },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken });
} catch {
res.status(401).json({ message: 'Refresh token yaroqsiz' });
}
});
Tez-tez uchraydigan xatolar
Maxfiy kalitni kod ichiga yozish:
// Xato — GitHub ga chiqsa, hamma token yaratishi mumkin:
jwt.sign(payload, 'mysecret');
// To'g'ri:
jwt.sign(payload, process.env.JWT_SECRET);
Payload ga maxfiy ma'lumot yozish:
JWT payload — ochiq. Base64 bilan kodlangan, lekin shifrlangan emas. Istalgan kishi atob() bilan o'qiy oladi. Parol, kredit karta — yozilmaydi.
Token muddatini juda uzun qo'yish:
jwt.sign(payload, SECRET, { expiresIn: '365d' });
// Token o'g'irlansa — 1 yil ishlatilishi mumkin
Access token — qisqa (15 daqiqa — 1 soat). Refresh token — o'rtacha (7-30 kun).
Xulosa
JWT — server hech narsa saqlamaydi, token ichida kim ekani yozilgan. Header + Payload + Signature. Signature tufayli token o'zgartirilganini bilish mumkin.
Access token — qisqa muddat, har so'rovda yuboriladi. Refresh token — uzun muddat, faqat access token yangilash uchun.
Maxfiy kalit .env da, payload da maxfiy ma'lumot yozilmaydi.
Keyingi dars
21-dars: bcrypt — parolni hash qilish, solishtirish