NODE.JS JWT AUTHENTICATION KURMAK
Modern web ve mobil uygulamalarda kullanıcı kimlik doğrulamanın en yaygın yöntemi JWT (JSON Web Token). Cookie-session tabanlı eski model sunucuda state tutar; JWT stateless çalışır. Token sunucu tarafında saklanmaz; kullanıcı her istekte token gönderir, sunucu doğrular, işine bakar.
Bu yaklaşımın avantajı ölçeklenebilirlik; yüz farklı sunucu aynı token'ı doğrulayabilir, sticky session gerekmez. Dezavantajı ise iptal mekanizmasının karmaşık olması; bir token çalındıysa expire olana kadar geçerli kalır.
Aşağıda JWT'nin nasıl çalıştığını, Node.js'te kurulum adımlarını, access/refresh token stratejisini ve güvenli saklama yöntemlerini anlatıyoruz.
JWT nedir?
JSON Web Token; üç parçadan oluşan kodlanmış string:
header.payload.signatureÖrnek:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0In0.aBcDeFgHiJÜç parça
- Header: Algoritma ve token tipi (HS256, RS256 vs)
- Payload: Kullanıcı bilgisi (user_id, email, role)
- Signature: Header + payload'ın secret key ile imzalı hash'i
Header ve payload Base64-encoded; herkes okuyabilir. Signature ise secret key gerektirir; sahte token üretmek imkansız. crypto modülü ve güvenli secret üretimi gibi konularda resmi Node.js dokümantasyonu ayrıntılı örnekler sunar.
JWT vs Session
| Özellik | JWT | Session |
|---|---|---|
| State | Stateless (sunucuda saklanmaz) | Stateful (sunucuda saklanır) |
| Ölçeklenebilirlik | Çok yüksek | Sticky session gerekir |
| Revocation | Karmaşık | Kolay (DB'den sil) |
| Boyut | Büyük (~500 byte) | Küçük (session ID) |
| Mobil uyum | Mükemmel | Zor |
Node.js JWT kurulumu
jsonwebtoken paketi en yaygın:
npm install jsonwebtoken bcryptToken üretme (login)
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
async function login(req, res) {
const { email, password } = req.body;
// 1. Kullanıcıyı bul
const user = await db.users.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Geçersiz bilgiler' });
}
// 2. Şifre kontrol
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(401).json({ error: 'Geçersiz bilgiler' });
}
// 3. Token üret
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
}Token doğrulama (middleware)
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token gerekli' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token süresi doldu' });
}
res.status(403).json({ error: 'Geçersiz token' });
}
}
// Kullanım
app.get('/api/profile', authenticate, (req, res) => {
res.json({ userId: req.user.userId });
});Access ve Refresh Token stratejisi
Tek token modeli güvenlik sorunu yaratır: token uzun süreli olursa çalınma riski; kısa süreli olursa kullanıcı sürekli login. Çözüm: iki token.
- Access token: Kısa süreli (15 dakika); API çağrılarında kullanılır
- Refresh token: Uzun süreli (7-30 gün); yeni access token almak için kullanılır
Login response
const accessToken = jwt.sign(
{ userId: user.id },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
// Refresh token'ı DB'ye kaydet (revocation için)
await db.refreshTokens.insert({ token: refreshToken, userId: user.id });
res.json({ accessToken, refreshToken });Refresh endpoint
app.post('/api/refresh', async (req, res) => {
const { refreshToken } = req.body;
// 1. DB'de var mı kontrol
const stored = await db.refreshTokens.findOne({ token: refreshToken });
if (!stored) {
return res.status(403).json({ error: 'Geçersiz refresh token' });
}
// 2. Token doğrula
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
// 3. Yeni access token üret
const newAccessToken = jwt.sign(
{ userId: decoded.userId },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m' }
);
res.json({ accessToken: newAccessToken });
} catch (error) {
res.status(403).json({ error: 'Refresh token süresi doldu' });
}
});
Güvenli saklama
Frontend (browser)
Üç seçenek var:
- localStorage: Kolay ama XSS saldırılarına açık
- sessionStorage: Tab kapanınca silinir; XSS riski hâlâ var
- httpOnly cookie: En güvenli; JavaScript erişemez
Modern öneri: access token in-memory (state), refresh token httpOnly secure cookie.
Cookie ile saklama
res.cookie('refreshToken', refreshToken, {
httpOnly: true, // JavaScript erişemez
secure: true, // Sadece HTTPS
sameSite: 'strict', // CSRF koruması
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 gün
});Token revocation (iptal)
JWT'nin en zor problemi; token üretildikten sonra expire olana kadar geçerli. İptal etmek için:
Refresh token blacklist
Logout veya güvenlik sebebiyle iptal edilen refresh token'ları DB'de "iptal" olarak işaretle. Refresh endpoint'i kontrol etsin.
app.post('/api/logout', async (req, res) => {
const { refreshToken } = req.body;
await db.refreshTokens.deleteOne({ token: refreshToken });
res.json({ message: 'Çıkış yapıldı' });
});Access token kısa süreli
Kısa süreli access token 15 dakikalık olursa iptal etme ihtiyacı pratik olarak azalır; en kötü 15 dakika sonra geçersiz.
Token blacklist (extreme durumlar için)
Acil iptal gerekiyorsa Redis'te blacklist tutulur. Her istekte token blacklist'te mi kontrol edilir.
const redis = require('redis');
const client = redis.createClient();
async function checkBlacklist(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
const isBlacklisted = await client.get(`blacklist:${token}`);
if (isBlacklisted) {
return res.status(403).json({ error: 'Token iptal edildi' });
}
next();
}Payload tasarımı
JWT payload'a ne konulmalı?
Konulmalı
- userId
- role (admin, user)
- iat (issued at, otomatik)
- exp (expiration, otomatik)
Konulmamalı
- Şifre (asla)
- Kart bilgisi
- Çok fazla kişisel veri (token boyutunu büyütür)
- Sık değişen veri (token'da güncel olmaz)
JWT payload herkes tarafından okunabilir (Base64 decode); hassas bilgi koymak ciddi güvenlik riskidir.
HS256 vs RS256
| HS256 | RS256 | |
|---|---|---|
| Algoritma | Symmetric (HMAC) | Asymmetric (RSA) |
| Key | Tek secret key | Public/private key çifti |
| Üretme | Secret ile | Private key ile |
| Doğrulama | Aynı secret ile | Public key ile |
| Kullanım | Monolith | Microservices |
Tek bir sunucu için HS256 yeterli; mikroservis mimaride asimetrik RS256 daha uygun (token üreten ve doğrulayan farklı servisler).
Yaygın güvenlik hataları
- Zayıf secret key. "123456" yerine cryptographic random (32+ karakter)
- Secret'i hardcoded yazmak. .env dosyasında tut
- localStorage'da token saklamak. XSS riski; httpOnly cookie tercih edilmeli
- HTTPS olmadan kullanmak. Token açıkta gider; ortadaki adam saldırıları
- Token'da hassas veri. Payload base64; herkes okur
- Çok uzun expiration. 1 yıllık token = 1 yıllık güvenlik açığı
- Refresh token rotation yapmamak. Her kullanımda yeni refresh token üret; eskisi iptal
Refresh token rotation
İleri güvenlik pratiği; her refresh işleminde hem access hem refresh token yeniden üretilir:
app.post('/api/refresh', async (req, res) => {
const { refreshToken: oldToken } = req.body;
// Eski token'ı doğrula
const decoded = jwt.verify(oldToken, process.env.JWT_REFRESH_SECRET);
// Eski token'ı invalidate et
await db.refreshTokens.deleteOne({ token: oldToken });
// Yeni token çifti üret
const newAccess = jwt.sign({ userId: decoded.userId }, ACCESS_SECRET, { expiresIn: '15m' });
const newRefresh = jwt.sign({ userId: decoded.userId }, REFRESH_SECRET, { expiresIn: '7d' });
await db.refreshTokens.insert({ token: newRefresh, userId: decoded.userId });
res.json({ accessToken: newAccess, refreshToken: newRefresh });
});Çalınmış token tespit edildiğinde tüm zincir invalidate edilebilir.

Sürdürme Yolu
JWT authentication modern API geliştirmenin temelidir. Daha kapsamlı bir pratik için kapsamlı Node.js eğitimi authentication ve güvenli API tasarımı konularını birlikte aktarır.
Yapının Özeti
JWT authentication stateless token tabanlı modern kimlik doğrulama. jsonwebtoken paketi ile kolay kurulum; access (kısa) + refresh (uzun) token stratejisi standart. Frontend'de httpOnly cookie tercih edilir; localStorage XSS risklidir. Refresh token rotation ile çalınmış token tespiti mümkün. Payload'a hassas veri konulmaz; secret key güçlü ve .env dosyasında. HTTPS olmadan asla kullanılmaz.



