Foruma hoş geldin 👋, Ziyaretçi

Forum içeriğine ve tüm hizmetlerimize erişim sağlamak için foruma kayıt olmalı ya da giriş yapmalısınız. Foruma üye olmak tamamen ücretsizdir.

Refresh token'ları veritabanında blacklist yönetimi yapmadan nasıl geçersiz kıldığım

codexor

Üye
Katılım
14 Mart 2026
Mesajlar
29
Merhaba arkadaşlar, bugün başımı çok ağrıtan bir sorunu ve bulduğum temiz çözümü anlatacağım. JWT tabanlı kimlik doğrulama sistemlerinde, refresh token'ların yönetimi her zaman bir baş ağrısıdır. Özellikle bir kullanıcı çıkış yaptığında veya bir token'ı iptal etmek istediğimizde, genellikle bir "blacklist" (kara liste) tablosu oluşturup veritabanında sorgu yapmamız gerekir. Bu, performans ve ölçeklenebilirlik açısından pek de hoş olmayan bir yöntem. Ben de "Acaba veritabanına dokunmadan, stateless bir şekilde bu işi çözebilir miyiz?" diye düşündüm ve işte karşıma çıkan yöntem.

🔥 Karşılaştığım Sorun

Projemde Node.js (Express) ve JWT kullanıyordum. Klasik akış şuydu: Access token süresi kısaydı (15 dk), refresh token süresi uzundu (7 gün). Kullanıcı çıkış yaptığında, o refresh token'ı veritabanındaki bir `blacklisted_tokens` tablosuna kaydediyorduk. Her token refresh isteğinde, gelen refresh token'ın bu listede olup olmadığını kontrol ediyorduk. Kullanıcı sayısı arttıkça bu tablo şişmeye ve her refresh işleminde ekstra bir database sorgusu yapmaya başladık. Bu durumdan mustarip olan var mı?

💡 Aklıma Gelen Çözüm: Token Versiyonlama (Token Versioning)

Araştırmalarım sonucu "refresh token rotation" ve "token versioning" kavramlarına denk geldim. Temel fikir şu: Her kullanıcının veritabanında bir `tokenVersion` (ya da `jwtSecretVersion`) alanı tutmak. Refresh token'ı imzalarken (signing) içine bu version bilgisini de gömüyoruz. Kullanıcı çıkış yaptığında veya tüm cihazlardan çıkış yapmasını istediğimizde, basitçe veritabanındaki bu `tokenVersion` değerini bir artırıyoruz.

Böylece, eski version numarası ile imzalanmış tüm refresh token'lar bir sonraki kontrol anında geçersiz hale geliyor. Sihir gibi! Veritabanında sadece bir alanı güncelliyoruz, kocaman bir blacklist tablosuyla uğraşmıyoruz.

🛠️ Nasıl Uyguladım? (Kod Zamanı!)

İlk olarak, kullanıcı şemasına basit bir version alanı ekledim.

JavaScript:
// Kullanıcı Modeli (Mongoose Örneği)
const userSchema = new mongoose.Schema({
  email: String,
  passwordHash: String,
  tokenVersion: { type: Number, default: 0 } // İşte kritik alan!
});

Sonra, refresh token oluştururken bu version bilgisini token'ın payload'ına ekliyorum.

JavaScript:
// Refresh Token Oluşturma
const generateRefreshToken = (user) => {
  const payload = {
    userId: user._id,
    version: user.tokenVersion // Version bilgisi payload'da
  };
  return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
    expiresIn: '7d'
  });
};

En can alıcı kısım, refresh token'ı doğrularken (verify) yapılan kontrol. Gelen token'ın içindeki version, veritabanındaki kullanıcının güncel `tokenVersion` değeri ile eşleşmeli.

JavaScript:
// Refresh Token Doğrulama ve Yeni Access Token Üretme
const refreshTokenHandler = async (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken) return res.sendStatus(401);

  try {
    // 1. Token'ı verify et
    const decoded = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);

    // 2. Kullanıcıyı bul
    const user = await User.findById(decoded.userId);
    if (!user) return res.sendStatus(403); // Forbidden

    // 3. TOKEN VERSION KONTROLÜ (Blacklist'e gerek kalmadan iptal mekanizması)
    if (decoded.version !== user.tokenVersion) {
      return res.sendStatus(403); // Token geçersiz! Version uyuşmuyor.
    }

    // 4. Versionlar uyuşuyorsa, yeni access token üret
    const newAccessToken = generateAccessToken(user);
    res.json({ accessToken: newAccessToken });

  } catch (error) {
    // Token süresi dolmuş veya geçersiz
    return res.sendStatus(403);
  }
};

Ve işte zafer anı! Kullanıcı çıkış yaptığında yapmam gereken tek şey:

JavaScript:
// Kullanıcı Çıkış (Logout) veya Tüm Cihazlardan Çıkış
const logout = async (req, res) => {
  try {
    // Sadece tokenVersion'ı bir artır. Eski token'lar kendiliğinden geçersiz olacak.
    await User.findByIdAndUpdate(req.userId, { $inc: { tokenVersion: 1 } });
    res.sendStatus(200);
  } catch (error) {
    res.sendStatus(500);
  }
};

🎯 Sonuç ve Düşüncelerim

Bu yöntemle birlikte:
- Blacklist tablosu ve ona ait ekstra sorgulardan kurtuldum.
- Çıkış işlemi inanılmaz hızlandı (sadece bir alanı güncelle).
- Sistem tamamen stateless kalmaya yaklaştı, sadece kritik bir noktada basit bir db güncellemesi yapılıyor.
- Güvenlik açısından da kullanıcı "tüm cihazlardan çıkış" yapmak istediğinde bu yöntem harika çalışıyor.

Tabii ki her çözümün bir trade-off'u var. Bu yöntemde, bir refresh token iptal edildiğinde, o kullanıcıya ait tüm refresh token'lar (diğer cihazlardaki veya tarayıcılardaki) de geçersiz hale geliyor. Yani kullanıcı sadece bir cihazdan değil, her yerden çıkış yapmış oluyor. Projenizin gereksinimlerine göre bu bir sorun teşkil edebilir veya etmeyebilir.

Siz daha önce JWT token yönetiminde benzer sorunlar yaşadınız mı? Blacklist dışında farklı, temiz çözümler deneyen oldu mu? Yorumlarda fikir alışverişi yapalım!
 

Tema özelleştirme sistemi

Bu menüden forum temasının bazı alanlarını kendinize özel olarak düzenleye bilirsiniz.

Zevkine göre renk kombinasyonunu belirle

Tam ekran yada dar ekran

Temanızın gövde büyüklüğünü sevkiniz, ihtiyacınıza göre dar yada geniş olarak kulana bilirsiniz.

Geri