🎯 Giriş
JWT (JSON Web Token), modern API'lerde en yaygın kullanılan durumsuz (stateless) kimlik doğrulama yöntemidir. Geleneksel session yapıları sunucuda durum saklamayı zorunlu kıldığı için, dağıtık ve mikro servis tabanlı sistemlerde ek yük oluşturur. JWT ise bu durumu ortadan kaldırarak sunucunun durum tutmadan isteği doğrulamasını sağlar.
Bu rehberde Express.js ile uçtan uca JWT tabanlı kimlik doğrulama kuracak, erişim–yenileme token mantığını uygulayacak ve tüm güvenlik aşamalarını öğreneceksiniz.
Bu Rehberde Ne Öğreneceksiniz?
- JWT yapısı (Header, Payload, Signature)
- Access & Refresh token mimarisi
- authenticateToken middleware yazımı
- HttpOnly cookie ile güvenli refresh token saklama
- Token yenileme akışı (/auth/refresh)
- Token versioning ile replay saldırılarını engelleme
- Rol tabanlı yetkilendirme (RBAC)
1. Express.js Projesini Oluşturma
Proje başlatma
npm init -y
ES Modules açma
"type": "module"
Gerekli paketler
npm install express dotenv jsonwebtoken cookie-parser
Basit proje yapısı
src/
app.js
middleware/
routes/
controllers/
config/
2. Express Başlangıç Dosyası
import express from 'express';
import dotenv from 'dotenv';
// Ortam değişkenlerini yükler
dotenv.config();
const app = express();
// JSON gövdesini ayrıştırır
app.use(express.json());
// Sunucuyu başlatır
app.listen(3000, () => console.log('Sunucu 3000 portunda çalışıyor'));
3. JWT Yapısını Anlamak (GÜÇLENDİRİLMİŞ Bölüm)
JWT üç bölümden oluşur:
| Bölüm | Açıklama | Örnek |
|---|---|---|
| Header | Algoritma & token türü | { "alg": "HS256", "typ": "JWT" } |
| Payload | Kullanıcı kimliği + claims | { "sub": "123", "role": "user", "exp": 1767215999 } |
| Signature | Header + Payload'ın gizli anahtarla imzası | Kriptografik hash |
✔ Payload şifreli değildir, sadece Base64URL kodludur. ✔ Hassas veri ASLA payload’a eklenmez.
4. Access Token Oluşturma
JWT_SECRET="guclu-gizli-anahtar"
import jwt from "jsonwebtoken";
// Access token üretir (15 dk)
function generateAccessToken(userId, role) {
const payload = { sub: userId, role };
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: "15m",
});
return token;
}
✔ Bu token kısa süreli olmalı. ✔ Çalınırsa zararı düşük olur.
5. JWT Doğrulama Middleware’i
import jwt from "jsonwebtoken";
// Korunan rotalar için doğrulama middleware'i
export function authenticateToken(req, res, next) {
const header = req.headers["authorization"];
const token = header?.split(" ")[1]; // Bearer kısmını atla
if (!token)
return res.status(401).json({ message: "Token bulunamadı" });
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err)
return res.status(403).json({ message: "Token geçersiz" });
req.user = decoded;
next();
});
}
✔ Token yok → 401 Unauthorized ✔ Token var ama geçersiz → 403 Forbidden
6. Access Token & Refresh Token Mimarisı
| Token | Süre | Amaç | Saklama |
|---|---|---|---|
| Access | 15 dk | API isteklerini doğrulama | Bellek / Authorization header |
| Refresh | 7 gün | Yeni access token alma | HttpOnly + Secure cookie |
✔ Refresh token kesinlikle localStorage’a konmaz. ✔ XSS’den korunmak için HttpOnly şarttır.
7. Refresh Token Endpoint (/auth/refresh)
import jwt from "jsonwebtoken";
// HttpOnly cookie okumak için cookie-parser gerekir
export async function refreshTokenHandler(req, res) {
const refresh = req.cookies?.refreshToken;
if (!refresh)
return res.status(401).json({ message: "Refresh token yok" });
// Refresh doğrulama
jwt.verify(refresh, process.env.REFRESH_SECRET, (err, decoded) => {
if (err)
return res.status(403).json({ message: "Refresh token geçersiz" });
// Yeni access token oluştur
const newAccess = jwt.sign(
{ sub: decoded.sub, role: decoded.role },
process.env.JWT_SECRET,
{ expiresIn: "15m" }
);
res.json({ accessToken: newAccess });
});
}
8. Token Versioning & Replay Saldırısı Koruması (GÜÇLENDİRİLMİŞ Bölüm)
Uzun ömürlü refresh token’lar çalınmaya çok uygundur. Bunu engellemenin iki yolu vardır:
1. Token Versioning (En güçlü yöntem)
Veritabanına:
user.tokenVersion = 1
Refresh token içine:
{ "sub": "123", "role": "user", "ver": 1 }
Yenileme geldiğinde:
Token içindeki ver
DB’deki tokenVersion ile karşılaştırılır
Yeni refresh token üretildiğinde version → +1 yapılır
Eski tokenlar otomatik çöp olur
2. Revocation List (İptal Listesi)
Refresh token’a bir UUID verilir. Logout olduğunda bu ID iptal listesine eklenir.
Redis burada en hızlı çözümdür.
9. Rol Bazlı Yetkilendirme (RBAC)
export function adminOnly(req, res, next) {
if (req.user?.role !== "admin") {
return res.status(403).json({ message: "Yetkin yok" });
}
next();
}
Kullanımı:
app.get("/admin/panel", authenticateToken, adminOnly, (req, res) => {
res.json({ message: "Admin paneline hoş geldin!" });
});
10. Güvenlik Önerileri (Güncellenmiş)
Access token → kısa ömürlü
Refresh token → HttpOnly + Secure cookie
Gizli anahtarlar .env içinde
Algoritma olarak HS256 veya RS256 kullan
Payload’a gizli veri koyma
Refresh token için versioning kullan
HTTPS zorunlu olmalı
JWT’yi localStorage’a koyma
11. Sıkça Sorulan Sorular
1. JWT şifreli mi?
Hayır, sadece imzalıdır.
2. Neden iki token var?
Access kısa ömürlü, refresh uzun ömürlüdür. Dengeli güvenlik sağlar.
3. Refresh token nerede saklanır?
HttpOnly + Secure cookie.
4. Süresi dolmuş token nasıl anlaşılır?
jwt.verify otomatik hata döndürür.
✔ Sonuç
Bu entegre rehber, Google sıralamasında daha güçlü olabilir çünkü:
Bölüm geçişleri güçlendirildi
Token versioning detaylandırıldı
JWT yapısı tablo halinde açıklandı
Rabisu Bulut üzerinde Node.js projelerini direkt test edebilir ve dağıtabilirsiniz. 🚀🔥