Node.js Authentication: JWT Token Sistemi

Node.js Authentication: JWT Token Sistemi
Modern web uygulamalarında kullanıcı kimlik doğrulama (authentication) ve yetkilendirme (authorization), güvenliğin temel taşlarıdır. JWT (JSON Web Token), stateless authentication sağlayan, kompakt ve self-contained bir token formatıdır. Bu rehberde Node.js ve Express.js ile JWT tabanlı bir authentication sistemi kuracak, access ve refresh token mekanizmasını, güvenli şifre yönetimini ve rol bazlı erişim kontrolünü detaylı olarak inceleyeceğiz.
1. JWT Nedir ve Nasıl Çalışır?
JWT, üç bölümden oluşan Base64 ile kodlanmış bir string'dir: Header (algoritma ve token türü), Payload (kullanıcı bilgileri ve claims) ve Signature (doğrulama imzası). Sunucu, JWT'yi oluştururken bir secret key ile imzalar; her istekte istemci bu token'ı gönderir ve sunucu imzayı doğrulayarak kullanıcıyı tanır. Session tabanlı authentication'dan farklı olarak, sunucu tarafında herhangi bir state tutulmaz.
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const crypto = require('crypto');
require('dotenv').config();
const app = express();
app.use(express.json());
// Yapılandırma
const config = {
accessTokenSecret: process.env.JWT_ACCESS_SECRET,
refreshTokenSecret: process.env.JWT_REFRESH_SECRET,
accessTokenExpiry: '15m',
refreshTokenExpiry: '7d',
saltRounds: 12
};
// Basit in-memory store (production'da veritabanı kullanın)
const users = [];
const refreshTokens = new Set();
// Token oluşturma fonksiyonları
function generateAccessToken(user) {
return jwt.sign(
{ userId: user.id, email: user.email, role: user.role },
config.accessTokenSecret,
{ expiresIn: config.accessTokenExpiry, issuer: 'my-app' }
);
}
function generateRefreshToken(user) {
const token = jwt.sign(
{ userId: user.id, tokenVersion: user.tokenVersion },
config.refreshTokenSecret,
{ expiresIn: config.refreshTokenExpiry }
);
refreshTokens.add(token);
return token;
}
function generateTokenPair(user) {
return {
accessToken: generateAccessToken(user),
refreshToken: generateRefreshToken(user),
expiresIn: 900 // 15 dakika (saniye cinsinden)
};
}
2. Kayıt ve Giriş Endpoint'leri
Kullanıcı kaydında şifreler bcrypt ile hash'lenerek saklanır. Giriş işleminde şifre doğrulaması yapılır ve başarılı ise token çifti döndürülür. Her aşamada güvenlik kontrolleri uygulamak kritik öneme sahiptir.
// Kayıt (Register)
app.post('/api/auth/register', async (req, res) => {
try {
const { name, email, password } = req.body;
// Email kontrolü
if (users.find(u => u.email === email)) {
return res.status(409).json({
success: false,
message: 'Bu email adresi zaten kayıtlı'
});
}
// Şifre gücü kontrolü
if (password.length < 8 || !/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) {
return res.status(400).json({
success: false,
message: 'Şifre en az 8 karakter, büyük/küçük harf ve rakam içermelidir'
});
}
// Şifreyi hash'le
const hashedPassword = await bcrypt.hash(password, config.saltRounds);
const user = {
id: crypto.randomUUID(),
name,
email: email.toLowerCase(),
password: hashedPassword,
role: 'user',
tokenVersion: 0,
createdAt: new Date().toISOString()
};
users.push(user);
const tokens = generateTokenPair(user);
res.status(201).json({
success: true,
message: 'Kayıt başarılı',
data: {
user: { id: user.id, name: user.name, email: user.email, role: user.role },
...tokens
}
});
} catch (error) {
res.status(500).json({ success: false, message: 'Sunucu hatası' });
}
});
// Giriş (Login)
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = users.find(u => u.email === email.toLowerCase());
if (!user) {
return res.status(401).json({
success: false,
message: 'Geçersiz email veya şifre'
});
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({
success: false,
message: 'Geçersiz email veya şifre'
});
}
const tokens = generateTokenPair(user);
res.json({
success: true,
message: 'Giriş başarılı',
data: {
user: { id: user.id, name: user.name, email: user.email, role: user.role },
...tokens
}
});
} catch (error) {
res.status(500).json({ success: false, message: 'Sunucu hatası' });
}
});
3. Token Yenileme ve Çıkış İşlemi
Refresh token mekanizması, kullanıcının sürekli yeniden giriş yapmasını önler. Access token süresi dolduğunda, istemci refresh token ile yeni bir access token alır. Çıkış işleminde refresh token invalidate edilmelidir.
// Token yenileme
app.post('/api/auth/refresh', (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken || !refreshTokens.has(refreshToken)) {
return res.status(401).json({
success: false,
message: 'Geçersiz refresh token'
});
}
try {
const decoded = jwt.verify(refreshToken, config.refreshTokenSecret);
const user = users.find(u => u.id === decoded.userId);
if (!user || user.tokenVersion !== decoded.tokenVersion) {
refreshTokens.delete(refreshToken);
return res.status(401).json({
success: false,
message: 'Token geçersiz kılınmış'
});
}
// Eski refresh token'ı sil, yeni çift oluştur
refreshTokens.delete(refreshToken);
const tokens = generateTokenPair(user);
res.json({
success: true,
data: tokens
});
} catch (error) {
refreshTokens.delete(refreshToken);
res.status(401).json({ success: false, message: 'Token süresi dolmuş' });
}
});
// Çıkış (Logout)
app.post('/api/auth/logout', authenticate, (req, res) => {
const { refreshToken } = req.body;
if (refreshToken) {
refreshTokens.delete(refreshToken);
}
// Tüm oturumları kapatmak için tokenVersion artır
const user = users.find(u => u.id === req.user.id);
if (user) {
user.tokenVersion += 1;
}
res.json({
success: true,
message: 'Çıkış başarılı'
});
});
4. Authentication ve Authorization Middleware
Authentication middleware, korunan endpoint'lere erişimi kontrol eder. Authorization middleware ise kullanıcının belirli bir işlemi yapmaya yetkisi olup olmadığını denetler. Bu iki kavram birbirinden farklıdır: authentication "kim olduğunuzu", authorization "ne yapabileceğinizi" belirler.
// Authentication middleware
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({
success: false,
message: 'Authorization header gerekli (Bearer token)'
});
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, config.accessTokenSecret);
req.user = decoded;
next();
} catch (error) {
const message = error.name === 'TokenExpiredError'
? 'Token süresi dolmuş, lütfen yenileyin'
: 'Geçersiz token';
return res.status(401).json({ success: false, message });
}
}
// Role-based authorization
function authorize(...roles) {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({
success: false,
message: `Bu işlem '${roles.join("' veya '")}' rolü gerektirir`
});
}
next();
};
}
// Kullanım örnekleri
app.get('/api/profile', authenticate, (req, res) => {
const user = users.find(u => u.id === req.user.userId);
res.json({ success: true, data: { name: user.name, email: user.email } });
});
app.get('/api/admin/users', authenticate, authorize('admin'), (req, res) => {
const safeUsers = users.map(({ password, ...u }) => u);
res.json({ success: true, data: safeUsers });
});
app.delete('/api/admin/users/:id',
authenticate,
authorize('admin'),
(req, res) => {
// Admin kullanıcı silme işlemi
res.json({ success: true, message: 'Kullanıcı silindi' });
}
);
5. Güvenlik En İyi Pratikleri
JWT tabanlı authentication sisteminizin güvenliğini artırmak için şu önlemleri alın: şifreleri her zaman bcrypt veya argon2 ile hash'leyin, token'larda hassas bilgi (şifre, kredi kartı) saklamayın, HTTPS kullanın, rate limiting uygulayın ve brute force koruması ekleyin. Ayrıca token'ların sadece gerekli bilgileri içermesine dikkat edin; payload ne kadar küçük olursa token o kadar hızlı işlenir ve her istekte daha az bant genişliği kullanılır.
CORS yapılandırmanızı sıkılaştırın, Content Security Policy header'larını ekleyin ve düzenli olarak bağımlılıklarınızı güvenlik açıklarına karşı tarayın. npm audit komutu, bilinen güvenlik açıklarını tespit etmenize yardımcı olur.
Sonuç
JWT tabanlı authentication sistemi, doğru uygulandığında güvenli ve ölçeklenebilir bir kimlik doğrulama çözümü sunar. Bu rehberde ele aldığımız konular — token oluşturma, kayıt/giriş akışları, token yenileme ve rol bazlı erişim kontrolü — modern bir web uygulamasının authentication altyapısının temellerini oluşturur. Güvenlik konusunda hiçbir zaman taviz vermemeli ve sisteminizi düzenli olarak güvenlik açıklarına karşı denetlemelisiniz.