Modern web uygulamalarında güvenli kimlik doğrulama için JWT (JSON Web Token) en yaygın kullanılan yöntemlerden biridir. Bu adım adım rehberde, Node.js ile bir REST API'de JWT tabanlı kimlik doğrulamanın nasıl uygulanacağını, token oluşturma, doğrulama, yenileme ve güvenlik iyileştirmelerini detaylandıracağız.
Adım 1: Proje Ortamını Hazırlayın
Öncelikle Node.js projenizi başlatın ve gerekli bağımlılıkları yükleyin. Modern JavaScript özelliklerinden yararlanmak için ES Modules kullanmanızı öneririz. Bu konuda daha fazla bilgi için CommonJS vs ES Modules yazımıza göz atabilirsiniz.
- Proje oluşturun:
mkdir jwt-auth-app && cd jwt-auth-app && npm init -y - Gerekli paketleri yükleyin:
npm install express jsonwebtoken bcryptjs dotenv - Geliştirme bağımlılığı olarak nodemon ekleyin:
npm install --save-dev nodemon - package.json dosyasına
"type": "module"ekleyerek ES Modules kullanımını etkinleştirin.
Adım 2: Ortam Değişkenlerini Ayarlayın
JWT sırları ve veritabanı bağlantı bilgileri gibi hassas verileri çevre değişkenlerinde saklayın. Proje kökünde bir .env dosyası oluşturun:
PORT=3000
JWT_SECRET=supersecretkey123
JWT_REFRESH_SECRET=refreshsecretkey456
ACCESS_TOKEN_EXPIRY=15m
REFRESH_TOKEN_EXPIRY=7d
Adım 3: Kullanıcı Modeli ve Kayıt İşlemini Oluşturun
Basit bir kullanıcı modeli için geçici bir dizi kullanacağız. Gerçek projelerde veritabanı kullanmalısınız. Şifreleri bcryptjs ile hashleyin.
// models/User.js
import bcrypt from 'bcryptjs';
const users = [];
export default users;
export const createUser = async (username, email, password) => {
const hashedPassword = await bcrypt.hash(password, 10);
const user = { id: users.length + 1, username, email, password: hashedPassword };
users.push(user);
return user;
};
export const findUserByEmail = (email) => users.find(user => user.email === email);
Adım 4: JWT Token Oluşturma Fonksiyonlarını Yazın
Access ve refresh token'ları oluşturan yardımcı fonksiyonları ayrı bir modülde toplayın.
// utils/jwt.js
import jwt from 'jsonwebtoken';
export const generateAccessToken = (user) => {
return jwt.sign({ id: user.id, email: user.email }, process.env.JWT_SECRET, { expiresIn: process.env.ACCESS_TOKEN_EXPIRY });
};
export const generateRefreshToken = (user) => {
return jwt.sign({ id: user.id }, process.env.JWT_REFRESH_SECRET, { expiresIn: process.env.REFRESH_TOKEN_EXPIRY });
};
export const verifyAccessToken = (token) => {
return jwt.verify(token, process.env.JWT_SECRET);
};
export const verifyRefreshToken = (token) => {
return jwt.verify(token, process.env.JWT_REFRESH_SECRET);
};
Adım 5: Authentication Middleware'ini Oluşturun
Korumalı rotalarda kullanılacak bir middleware yazın.
// middleware/auth.js
import { verifyAccessToken } from '../utils/jwt.js';
export const authenticate = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) return res.status(401).json({ message: 'Token bulunamadı' });
try {
const decoded = verifyAccessToken(token);
req.user = decoded;
next();
} catch (error) {
return res.status(403).json({ message: 'Token geçersiz veya süresi dolmuş' });
}
};
Adım 6: Rotaları Oluşturun (Kayıt, Giriş, Korumalı Kaynak)
Express rotalarını ayrı bir dosyada tanımlayın. N+1 problemi gibi performans sorunlarıyla karşılaşmamak için veritabanı sorgularını optimize etmeyi unutmayın; bu konuda Node.js'de N+1 Problemi yazımız size rehber olacaktır.
// routes/auth.js
import { Router } from 'express';
import { createUser, findUserByEmail } from '../models/User.js';
import { generateAccessToken, generateRefreshToken, verifyRefreshToken } from '../utils/jwt.js';
import bcrypt from 'bcryptjs';
import { authenticate } from '../middleware/auth.js';
const router = Router();
// Kayıt
router.post('/register', async (req, res) => {
try {
const { username, email, password } = req.body;
const existingUser = findUserByEmail(email);
if (existingUser) return res.status(400).json({ message: 'Bu e-posta zaten kayıtlı' });
const user = await createUser(username, email, password);
const accessToken = generateAccessToken(user);
const refreshToken = generateRefreshToken(user);
res.status(201).json({ accessToken, refreshToken });
} catch (error) {
res.status(500).json({ message: 'Kayıt sırasında hata oluştu' });
}
});
// Giriş
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = findUserByEmail(email);
if (!user) return res.status(400).json({ message: 'Kullanıcı bulunamadı' });
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) return res.status(400).json({ message: 'Geçersiz şifre' });
const accessToken = generateAccessToken(user);
const refreshToken = generateRefreshToken(user);
res.json({ accessToken, refreshToken });
} catch (error) {
res.status(500).json({ message: 'Giriş sırasında hata oluştu' });
}
});
// Token yenileme
router.post('/refresh', (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) return res.status(401).json({ message: 'Refresh token gerekli' });
try {
const decoded = verifyRefreshToken(refreshToken);
const user = { id: decoded.id };
const newAccessToken = generateAccessToken(user);
res.json({ accessToken: newAccessToken });
} catch (error) {
res.status(403).json({ message: 'Refresh token geçersiz veya süresi dolmuş' });
}
});
// Korumalı rota örneği
router.get('/profile', authenticate, (req, res) => {
res.json({ message: 'Profil sayfası', user: req.user });
});
export default router;
Adım 7: Sunucuyu Çalıştırın ve Test Edin
Ana dosyada Express sunucusunu başlatın ve rotaları mount edin.
// server.js
import express from 'express';
import dotenv from 'dotenv';
import authRoutes from './routes/auth.js';
dotenv.config();
const app = express();
app.use(express.json());
app.use('/api/auth', authRoutes);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Postman veya curl ile uç noktaları test edin. Kayıt ve giriş işlemlerinden sonra dönen access token ile /api/auth/profile rotasına istek atarak korumalı kaynağa erişin.
Sık Yapılan Hatalar ve Dikkat Edilmesi Gerekenler
- Token'ı istemci tarafında güvenli olmayan şekilde saklamak: localStorage yerine httpOnly cookie kullanmayı düşünün.
- Refresh token'ı veritabanında saklamamak: Refresh token'ları iptal edilebilir olmalı; bunun için bir whitelist veya blacklist mekanizması kurun.
- JWT sırrını zayıf seçmek: En az 256 bit uzunluğunda rastgele bir anahtar kullanın.
- Token sürelerini çok uzun tutmak: Access token için 15 dakika, refresh token için 7 gün makul değerlerdir.
- Hata mesajlarında detay vermek: Kötü niyetli kullanıcılara bilgi sızıntısını önlemek için genel hatalar döndürün.
Bu adımları izleyerek Node.js REST API'nize sağlam bir JWT kimlik doğrulama sistemi ekleyebilirsiniz. Daha karmaşık senaryolar için rol tabanlı yetkilendirme (RBAC) eklemeyi düşünebilirsiniz.
Sık Sorulan Sorular
JWT token'ını nerede saklamalıyım?
JWT token'larını localStorage yerine httpOnly çerezlerde saklamak daha güvenlidir. localStorage XSS saldırılarına karşı savunmasızdır. httpOnly çerezler JavaScript ile erişilemez, böylece saldırı yüzeyini azaltır.
Access token süresi dolduğunda ne yapmalıyım?
Access token süresi dolduğunda, refresh token kullanarak yeni bir access token alabilirsiniz. Uygulamanızda bir token yenileme (refresh) uç noktası oluşturun ve istemci tarafında otomatik yenileme mantığı kurun.
JWT güvenli midir?
JWT, doğru kullanıldığında güvenlidir. Ancak sır anahtarın güçlü olması, token sürelerinin kısa tutulması ve HTTPS kullanımı gibi önlemler alınmalıdır. Ayrıca refresh token'ların iptal edilebilir olması da önemlidir.
Birden fazla sunucuda JWT doğrulaması nasıl yapılır?
JWT, stateless bir yapıya sahiptir. Tüm sunucular aynı JWT sırrını paylaşırsa, herhangi bir sunucu token'ı doğrulayabilir. Sırrı güvenli bir ortam değişkeni veya bir anahtar yönetim hizmeti (ör. AWS KMS) ile dağıtabilirsiniz.






