Node.js İle Token Tabanlı Kimlik Doğrulama Nasıl Yapılır?

Node.js İle Token Tabanlı Kimlik Doğrulama Nasıl Yapılır?
Node.js İle Token Tabanlı Kimlik Doğrulama Nasıl Yapılır?

Ö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ışı

  1. Kullanıcı giriş yaptığında hem accessToken hem de refreshToken üretilir.
  2. refreshToken veritabanına (kullanıcı ID'si ile eşleşecek şekilde) kaydedilir.
  3. Access token süresi dolduğunda, istemci /refresh rotasına elindeki refresh token'ı gönderir.
  4. 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) yerine RS256 (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.

Bu yazıya tepkinizi paylaşın:
Kerem Aydın

Mutfağından bahçesine, yaşamın her alanında hızlı ve etkili çözüm rehberleri hazırlıyorum. Bilgi kirliliğinden uzak, doğrudan sonuca odaklanan içerikler üretiyorum.

Yorumlar (0)

Yorum Yaz