Authentication mı Authorization mı? JWT, OAuth ve Session Farkları
Bir uygulamaya giriş yapıyorsunuz. Bu işlem sırasında iki farklı şey oluyor ama çoğu geliştirici ikisini tek bir şey olarak görüyor. Bu ayrımı yapmadan güvenli sistem inşa etmek mümkün değil.
Temel Ayrım: Authentication vs Authorization
Authentication (Kimlik Doğrulama) — "Sen kimsin?" sorusunun cevabıdır. Kullanıcının iddia ettiği kişi olduğunu kanıtlama sürecidir. Email + şifre, biyometrik veri, SMS kodu — bunların hepsi authentication mekanizmalarıdır.
Authorization (Yetkilendirme) — "Ne yapabilirsin?" sorusunun cevabıdır. Kimliği doğrulanmış kullanıcının hangi kaynaklara, hangi işlemleri yapabileceğini belirleme sürecidir.
Gerçek hayat analojisi: Bir binaya girerken güvenlik görevlisine kimliğinizi gösteriyorsunuz — bu authentication. İçeri girdikten sonra hangi katlara, hangi odalara girebileceğinizi belirleyen kart sistemi — bu authorization. Kimliğiniz doğrulandı ama her yere giremezsiniz.
Authentication: Bu kullanıcı gerçekten ali@example.com mu? ✓
Authorization: ali@example.com admin paneline erişebilir mi? ✗
Session Tabanlı Authentication
Geleneksel web uygulamalarında session tabanlı authentication kullanılır. Kullanıcı giriş yaptığında sunucu bir session oluşturur, bunu veritabanında ya da Redis'te saklar ve kullanıcıya bir session ID gönderir. Bu ID genellikle cookie'de tutulur.
1. Kullanıcı email + şifre gönderir
2. Sunucu doğrular → session oluşturur (session_id: "abc123")
3. Session Redis'e yazılır
4. Tarayıcıya Set-Cookie: session_id=abc123 gönderilir
5. Sonraki her istekte tarayıcı bu cookie'yi otomatik gönderir
6. Sunucu session_id'yi Redis'te arar → kullanıcıyı bulur
// Express.js ile session örneği app.post('/login', async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user || !await bcrypt.compare(password, user.passwordHash)) { return res.status(401).json({ error: 'Invalid credentials' }); } req.session.userId = user.id; req.session.role = user.role; res.json({ message: 'Login successful' }); }); // Middleware: giriş yapılmış mı? const requireAuth = (req, res, next) => { if (!req.session.userId) { return res.status(401).json({ error: 'Unauthorized' }); } next(); };
Session tabanlı authentication'ın avantajı: logout anlıktır — session silindiğinde kullanıcı anında çıkmış olur. Dezavantajı: sunucu state tutar, her istek için session store'a gidilmesi gerekir. Yatay ölçeklendirmede (birden fazla sunucu) session paylaşımı sorun olabilir.
JWT: Stateless Authentication
JWT (JSON Web Token), sunucunun hiçbir şey saklamadığı stateless authentication çözümüdür. Token'ın içinde kullanıcı bilgileri zaten vardır ve sunucu bunu her seferinde doğrular.
JWT üç bölümden oluşur, nokta ile ayrılır:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOiIxMjMiLCJlbWFpbCI6ImFsaUBleGFtcGxlLmNvbSIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzA5NjQ4MDAwLCJleHAiOjE3MDk3MzQ0MDB9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header — algoritma bilgisi (base64 encode edilmiş) Payload — kullanıcı verisi (base64 encode edilmiş, şifreli değil!) Signature — header + payload'ın secret key ile imzalanmış hali
// JWT oluşturma ve doğrulama import jwt from 'jsonwebtoken'; // Token oluştur const token = jwt.sign( { userId: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET, { expiresIn: '24h' } ); // Token doğrula const decoded = jwt.verify(token, process.env.JWT_SECRET); // decoded: { userId: "123", email: "ali@example.com", role: "user", iat: ..., exp: ... }
JWT'nin büyük avantajı: sunucu hiçbir şey saklamaz. Token kendini taşır. Microservice mimarilerinde, farklı servisler aynı secret ile token'ı doğrulayabilir.
Kritik uyarı: JWT payload şifreli değildir, sadece encode edilmiştir. Base64 decode ile okunabilir. Payload'a şifre, kredi kartı numarası gibi hassas veri koymayın.
JWT'nin en büyük dezavantajı: token'ı geçersiz kılmak zordur. Kullanıcıyı ban'ladınız ama token'ı 23 saat daha geçerli. Çözüm: kısa ömürlü access token + uzun ömürlü refresh token kombinasyonu.
// Access + Refresh Token stratejisi const accessToken = jwt.sign(payload, secret, { expiresIn: '15m' }); const refreshToken = jwt.sign({ userId }, refreshSecret, { expiresIn: '7d' }); // Refresh token DB'ye kaydedilir (revoke edilebilir olması için) await RefreshToken.create({ token: refreshToken, userId });
OAuth 2.0: Üçüncü Taraf Yetkilendirme
"Google ile Giriş Yap" butonuna tıkladığınızda OAuth 2.0 devreye girer. OAuth, bir uygulamanın sizin adınıza başka bir servise sınırlı erişim almasını sağlayan yetkilendirme protokolüdür.
OAuth akışı (Authorization Code Flow):
1. Kullanıcı "Google ile Giriş Yap"a tıklar
2. Uygulama kullanıcıyı Google'a yönlendirir:
https://accounts.google.com/oauth/authorize?
client_id=YOUR_APP_ID&
redirect_uri=https://yourapp.com/callback&
scope=email profile&
response_type=code
3. Kullanıcı Google'da izin verir
4. Google, authorization code ile uygulamaya döner
5. Uygulama bu kodu Google'a göndererek access token alır
6. Access token ile Google API'sine erişilir
OAuth'u anlamak için şunu aklınızda tutun: OAuth bir authentication protokolü değil, authorization protokolüdür. "Bu uygulamanın senin Google verilerine erişmesine izin veriyor musun?" sorusuna cevap arar.
Authentication için OAuth üzerine inşa edilmiş OpenID Connect (OIDC) kullanılır. Google, GitHub, Microsoft ile "sosyal giriş" yapıldığında arka planda OIDC çalışır.
RBAC: Rol Tabanlı Yetkilendirme
Authorization'ın en yaygın implementasyonu Role-Based Access Control'dür. Kullanıcılara roller atanır, roller izinleri tanımlar.
// Rol ve izin tanımları const permissions = { admin: ['read', 'write', 'delete', 'manage_users'], editor: ['read', 'write'], viewer: ['read'] }; // Authorization middleware const requirePermission = (permission) => { return (req, res, next) => { const userPermissions = permissions[req.user.role] || []; if (!userPermissions.includes(permission)) { return res.status(403).json({ error: 'Forbidden: insufficient permissions' }); } next(); }; }; // Route'larda kullanım app.delete('/articles/:id', requireAuth, // önce kim olduğunu doğrula requirePermission('delete'), // sonra ne yapabileceğini kontrol et deleteArticleHandler );
401 vs 403 ayrımına dikkat: 401 Unauthorized kimlik doğrulama başarısız (giriş yapmamış), 403 Forbidden yetki yok (giriş yapmış ama izni yok). Bu iki status kodu sıklıkla karıştırılır.
Güvenlik Best Practice'leri
Şifreleri asla düz metin saklamayın. bcrypt veya argon2 ile hash'leyin. Bcrypt'in kasıtlı olarak yavaş olduğunu unutmayın — bu bir özellik, brute force saldırılarını zorlaştırır.
// Şifre hash'leme const hash = await bcrypt.hash(password, 12); // 12 = work factor // Şifre doğrulama const isValid = await bcrypt.compare(password, hash);
HTTPS kullanın. HTTP üzerinden gönderilen token'lar ve cookie'ler ağ trafiğini dinleyen herkes tarafından okunabilir.
Cookie'lerde HttpOnly ve Secure flag'lerini kullanın. HttpOnly JavaScript'in cookie'ye erişmesini engeller (XSS koruması). Secure cookie'nin sadece HTTPS üzerinden gönderilmesini zorlar.
res.cookie('session_id', sessionId, { httpOnly: true, // JS erişemez secure: true, // sadece HTTPS sameSite: 'strict' // CSRF koruması });
Authentication ve authorization doğru kurulmadan hiçbir uygulama güvenli değildir. Bu iki kavramı net ayırt etmek, güvenlik açıklarının büyük bölümünü baştan önler.