Ön Hazırlık ve Gereksinimler
Projenize başlamadan önce bilgisayarınızda Node.js'in 20.x veya daha yeni bir LTS sürümünün yüklü olması gerekmektedir. Bağımlılık yönetimi için npm (Node Package Manager) kullanacağız. Projenizi başlatmak için bir klasör oluşturun ve gerekli paketleri kurun.
# Proje klasörünü oluşturun
mkdir node-auth-api
cd node-auth-api
# Projeyi başlatın
npm init -y
# Gerekli paketleri yükleyin
npm install express jsonwebtoken bcryptjs dotenv
Burada kullandığımız jsonwebtoken paketi JWT oluşturmak ve doğrulamak için, bcryptjs ise kullanıcı şifrelerini güvenli bir şekilde hash'lemek (tek yönlü şifreleme) için kullanılır. dotenv ise ortam değişkenlerini yönetmemizi sağlar.
Kullanıcı Kaydı ve Şifre Güvenliği
Kullanıcı veritabanına şifreleri asla düz metin olarak kaydedemezsiniz. Bcrypt kütüphanesi, şifreleri "salt" (tuzlama) yöntemiyle karmaşık hale getirerek veritabanı sızıntısı durumunda bile şifrelerin çözülmesini imkansız kılar.
const bcrypt = require('bcryptjs');
async function registerUser(password) {
const saltRounds = 12; // Güvenlik için 12 tur yeterlidir
const hashedPassword = await bcrypt.hash(password, saltRounds);
return hashedPassword;
}
Yukarıdaki kod örneğinde, saltRounds değeri 12 olarak belirlenmiştir. Bu değer, şifreleme işleminin zorluğunu belirler. Çok düşük değerler kaba kuvvet (brute-force) saldırılarına kapı açarken, çok yüksek değerler sunucunun yanıt süresini gereksiz uzatır.
JWT Oluşturma ve Giriş İşlemi
Kullanıcı giriş yaptığında, kimliğini doğrulamak için ona bir JWT veririz. Bu token, sunucu tarafından imzalanmış bir veri paketidir. Kullanıcı her istekte bu token'ı "Authorization" başlığında gönderir.
const jwt = require('jsonwebtoken');
function generateToken(user) {
const payload = { id: user.id, email: user.email };
const secretKey = process.env.JWT_SECRET; // .env dosyasında saklanmalı
return jwt.sign(payload, secretKey, { expiresIn: '1h' });
}
Token'ın süresini expiresIn: '1h' parametresi ile 1 saat olarak kısıtladık. Bu, token çalınsa bile saldırganın elindeki erişimin sınırlı olmasını sağlar.
Kritik Güvenlik Uyarısı: JWT gizli anahtarınızı (JWT_SECRET) asla kaynak kodunuzun içine yazmayın. Mutlaka bir .env dosyasında tutun ve bu dosyayı .gitignore listesine ekleyin.
Korumalı Rotalar İçin Middleware Yazımı
Middleware (ara katman yazılımı), bir isteğin rotaya ulaşmadan önce geçmesi gereken güvenlik kontrol noktasıdır. Burada token'ın geçerli olup olmadığını kontrol ederiz.
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
Bu fonksiyon, token eksikse 401 (Unauthorized), token geçersizse 403 (Forbidden) hatası döner. Başarılı doğrulamada ise kullanıcı bilgisini req.user nesnesine ekleyerek bir sonraki fonksiyona aktarır.
Token Tabanlı Kimlik Doğrulama Yöntemleri Karşılaştırması
| Yöntem | Avantaj | Dezavantaj |
|---|---|---|
| JWT (Stateless) | Sunucu yükü az, ölçeklenebilir | Token iptali zordur |
| Session (Stateful) | Anında oturum sonlandırma | Sunucu belleği tüketimi |
Uygulama Testi ve Hata Ayıklama
Sistemi test etmek için Postman veya cURL kullanabilirsiniz. Giriş yaptıktan sonra dönen token'ı kopyalayın ve korumalı rotaya istek atarken "Authorization: Bearer [TOKEN]" başlığını eklemeyi unutmayın.
// Örnek bir korumalı rota kullanımı
app.get('/dashboard', authenticateToken, (req, res) => {
res.json({ message: "Hoş geldiniz, " + req.user.email });
});
Eğer "403 Forbidden" hatası alıyorsanız, token süresi dolmuş olabilir veya JWT_SECRET anahtarınız sunucu tarafında farklı tanımlanmış olabilir. Konsol logları ile err nesnesini incelemek hata ayıklamada en iyi dostunuzdur.
Önemli Not: Bu kod örneği eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce HTTPS protokolünü zorunlu kılmalı ve token saklama stratejilerini (HttpOnly cookie vs. LocalStorage) güvenlik gereksinimlerinize göre belirlemelisiniz.
Sıkça Sorulan Sorular
JWT neden localStorage'da saklanmamalıdır?
LocalStorage, XSS (Cross-Site Scripting) saldırılarına karşı savunmasızdır. Tarayıcıda çalışan kötü niyetli bir script, token'ınızı kolayca çalabilir. Bunun yerine HttpOnly ve Secure flag'lerine sahip çerezler kullanmak daha güvenlidir.
Token süresi dolduğunda ne yapmalıyım?
Kullanıcıya 401 hatası dönerek giriş sayfasına yönlendirmeli veya "Refresh Token" mekanizmasını kullanarak yeni bir erişim token'ı almasını sağlamalısınız.
JWT içeriği şifreli midir?
Hayır, JWT sadece imzalanmıştır (signed). İçeriği Base64 ile kodlanmıştır ve herkes tarafından okunabilir. Bu nedenle token içine asla şifre gibi hassas veriler koymayın.
Birden fazla sunucuda JWT nasıl çalışır?
JWT'nin en büyük avantajı budur. Sunucularınız aynı JWT_SECRET anahtarına sahip olduğu sürece, herhangi bir sunucu token'ı doğrulayabilir. Veritabanı sorgusuna gerek kalmaz.
Bcrypt maliyeti performansımı etkiler mi?
Evet, hashleme işlemi CPU yoğun bir işlemdir. Ancak bu, kaba kuvvet saldırılarını yavaşlattığı için güvenlik adına kabul edilebilir bir maliyettir.
Refresh Token Mekanizması ile Güvenliği Artırma
Kısa ömürlü erişim tokenları (Access Token) güvenliği sağlasa da, kullanıcı deneyimini olumsuz etkileyebilir. Kullanıcının her 15 dakikada bir tekrar giriş yapmasını engellemek için Refresh Token yapısını kurmalısınız. Refresh token, veritabanında saklanan ve yalnızca yeni bir access token almak için kullanılan uzun ömürlü bir anahtardır.
Refresh Token İş Akışı
- Kullanıcı giriş yaptığında hem
accessTokenhem derefreshTokenüretilir. refreshTokenveritabanına (kullanıcı ID'si ile eşleşecek şekilde) kaydedilir.- Access token süresi dolduğunda, istemci
/refreshrotasına elindeki refresh token'ı gönderir. - Sunucu, gönderilen token'ın veritabanındakiyle eşleşip eşleşmediğini kontrol eder ve yeni bir access token üretir.
// Refresh Token rotası örneği
app.post('/refresh', async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) return res.sendStatus(401);
const user = await db.findUserByToken(refreshToken);
if (!user) return res.sendStatus(403);
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
const accessToken = jwt.sign({ id: user.id }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
res.json({ accessToken });
});
});
Token İptal Mekanizması (Blacklisting)
JWT'ler doğası gereği "stateless" (durumsuz) olduğu için, süreleri dolmadan onları geçersiz kılmak zordur. Kullanıcı çıkış yaptığında veya şifresini değiştirdiğinde token'ı anında geçersiz kılmak için bir Blacklist stratejisi izlemelisiniz.
Redis ile Token Yönetimi
En performanslı yöntem, iptal edilen token'ları Redis gibi hızlı bir bellek içi veritabanında saklamaktır. Middleware katmanında her istekte token'ın kara listede olup olmadığı kontrol edilir.
// Middleware içerisinde kara liste kontrolü
const isBlacklisted = await redisClient.get(`blacklist:${token}`);
if (isBlacklisted) {
return res.status(401).json({ message: "Token geçersiz kılınmış." });
}
Profesyonel İpucu: Redis kullanırken token'ın süresi (TTL) dolduğunda otomatik silinmesi için EXPIRE komutunu kullanarak bellek yönetimini optimize edebilirsiniz.
Performans ve Ölçeklendirme İpuçları
Token tabanlı kimlik doğrulama sistemlerinde performans darboğazlarını önlemek için şu stratejileri uygulayın:
- Payload Boyutu: JWT içerisine gereksiz veri (büyük objeler, profiller) koymayın. Sadece kullanıcı ID'si ve yetki seviyesi gibi temel bilgileri tutun.
- Asimetrik İmzalama: Çok büyük ölçekli sistemlerde
HS256(simetrik) yerineRS256(asimetrik - Private/Public Key) kullanarak, token doğrulama işlemini sadece public key'e sahip olan mikroservislerin yapmasını sağlayabilirsiniz. - Veritabanı Okumaları: Her istekte kullanıcıyı veritabanından çekmek yerine, JWT içerisinde bulunan bilgileri kullanın. Veritabanı sorgusunu sadece kritik yetki kontrollerinde yapın.
Sonuç
Node.js ile token tabanlı kimlik doğrulama, modern web mimarilerinin temel taşıdır. Bu rehberde, güvenli bir kayıt süreci, JWT ile oturum yönetimi ve middleware kullanarak yetkilendirme adımlarını tamamladık. Bir sonraki adım olarak, sisteminize "Refresh Token" mekanizmasını entegre ederek kullanıcı deneyimini kesintisiz hale getirebilir ve token iptal süreçlerini (blacklist) yönetmeyi öğrenebilirsiniz.


Yorumlar (0)
Yorum Yaz