Tokenları Doğrulama
Coddy'nin Node.js Journey'sinin Kimlik Doğrulama ve Güvenlik bölümünün bir parçası. Ders 16 / 24.
Bir API istemcisi her istekte belirtecini bir başlıkta gönderir: Authorization: Bearer <token>. decodeJwt ile okumak, herhangi birinin yazdığı her belirtecin kabul edilmesine yol açar. jwtVerify(token, key) gizli anahtarınızla imzayı yeniden hesaplar ve imza eşleşmediğinde, belirtecin süresi dolduğunda veya belirteç bir JWT olmadığında hata verir. payload öğesine ancak bu işlem başarıyla tamamlandıktan sonra güvenebilirsiniz.
async function requireDevice(req, res, next) {
const token = (req.get('authorization') ?? '').replace(/^Bearer /, '');
try {
const { payload } = await jwtVerify(token, deviceKey);
req.device = payload.sub;
} catch {
return res.status(401).json({ error: 'Unknown device' });
}
next();
}Görev
KolayŞimdi imzaladığın belirteçleri kabul et. Bir uygulama, belirtecini her istekte bir üstbilgide gönderir ve sunucu, içindeki herhangi bir şeye güvenmeden önce imzayı denetler.
server.jsiçinde,josemodülündenjwtVerifyiçe aktar.requireUser(req, res, next)adlı bir ara yazılım yaz.Authorizationüstbilgisini okur. Bu üstbilgideBearersözcüğü, ardından bir boşluk ve sonra belirteç bulunur. Belirteci buradan al.- İmza atarken kullandığın anahtarın aynısını kullanarak belirteci
jwtVerifyile denetle. İmza eşleşmediğinde, belirtecin süresi dolduğunda veya metin hiç belirteç olmadığındajwtVerifyhata verir. Bu durumların tümünde ve üstbilgi olmadığında{"error": "Log in first"}yanıtını 401 durum koduyla gönder. - Belirteç geçerliyse
req.userdeğerini, belirtecinsubtalebinden alınanidveusernametalebinden alınanusernamealanlarını içeren bir nesneye ayarla. Ardındannext()çağrısını yap. requireUserileGET /meuç noktasını ekle. Bu uç noktareq.userdeğerini döndürür:{"id": "66f1c2a9e4b0a1b2c3d4e5f6", "username": "ada"}.
Deneyin: Çalıştır düğmesine bas. GET /me isteğini gönder: durum kodu 401 ve {"error": "Log in first"}. POST /signup isteğini, ardından da POST /token isteğini gönder; her ikisinde de {"username": "ada", "password": "correct horse"} kullan ve belirteci kopyala. Başlıklar alanına, adı Authorization olan bir üstbilgi ekle; değeri Bearer, bir boşluk ve belirtecinden oluşsun. GET /me isteğini gönder: durum kodu 200; kimliğin ve "username": "ada" yanıtla birlikte gelir. Şimdi belirtecin orta bölümündeki, iki noktanın arasındaki bir harfi değiştir ve GET /me isteğini yeniden gönder: imza artık eşleşmediği için durum kodu 401.
Kendin dene
import express from 'express';
import { SignJWT, decodeJwt } from 'jose';
import { users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const app = express();
app.use(express.json());
app.post('/signup', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const password = String(req.body.password ?? '');
if (!username || password.length < 8) {
return res.status(400).json({ error: 'Pick a username and a password of at least 8 characters' });
}
if (await users.findOne({ username })) {
return res.status(409).json({ error: 'That username is taken' });
}
await users.insertOne({ username, passwordHash: await hashPassword(password) });
res.status(201).json({ username });
});
app.post('/login', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
res.json({ username: user.username });
});
app.post('/token', async (req, res) => {
const username = String(req.body.username ?? '').trim().toLowerCase();
const user = await users.findOne({ username });
if (!user || !(await verifyPassword(String(req.body.password ?? ''), user.passwordHash))) {
return res.status(401).json({ error: 'Wrong username or password' });
}
const token = await new SignJWT({ username: user.username })
.setProtectedHeader({ alg: 'HS256' })
.setSubject(user._id.toHexString())
.setIssuedAt()
.setExpirationTime('1h')
.sign(secret);
res.json({ token });
});
// Bir token'ın içeriğini gösterir: GET /inspect?token=...
app.get('/inspect', (req, res) => {
try {
res.json(decodeJwt(String(req.query.token ?? '')));
} catch {
res.status(400).json({ error: 'Not a JWT' });
}
});
// Yönetici sayfası. İmzası doğrulanmadan hiçbir beyana güvenilemez.
app.get('/admin', (req, res) => {
res.status(403).json({ error: 'Admins only' });
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Kimlik Doğrulama ve Güvenlik bölümündeki tüm dersler
Kendi başına pratik yap: Express playground