توقيع الرموز
جزء من قسم المصادقة والأمان في رحلة Node.js على Coddy. الدرس 15 من 24.
لا تكون قيمة الرمز مهمة إلا إذا لم يستطع أي شخص آخر إنشاء مثله. باستخدام HS256، تمزج jose الترويسة والحمولة مع سر لا يعرفه إلا الخادم، ثم تحسب تجزئة للنتيجة. هذه التجزئة هي التوقيع. من دون السر، لا يستطيع أحد إنشاء رمز يطابقه.
يأتي السر من متغير بيئة، process.env.JWT_SECRET، وليس من الشيفرة مطلقًا: فالشيفرة تُشارك وتُنسخ وتُودَع في المستودع. يضبط هذا الدرس JWT_SECRET لك. وتحتاج jose إلى السر على هيئة بايتات.
const key = new TextEncoder().encode(process.env.INVITE_SECRET);
const invite = await new SignJWT({ room: 'design' })
.setProtectedHeader({ alg: 'HS256' })
.setSubject(guestId)
.setIssuedAt()
.setExpirationTime('2d')
.sign(key);تأخذ setSubject نصًا، بينما تأخذ setExpirationTime مدة مثل '30m' أو '2d'.
التحدي
سهلالتطبيقات التي تستدعي واجهة API الخاصة بك، مثل تطبيق الهاتف، لا تحتفظ بملفات تعريف الارتباط بالطريقة التي يفعلها المتصفح. أعطها رمزًا بدلًا من ذلك: JWT موقّعًا ترسله مع كل طلب. يُستخدم POST /token لتسجيل الدخول إلى تلك التطبيقات.
- في
server.js، استوردSignJWTمنjoseبجانبdecodeJwt. فوق المسارات، حوّلprocess.env.JWT_SECRETإلى بايتات باستخدامTextEncoder، كما يوضّح الدرس، لأنjoseيوقّع باستخدام البايتات. يضبط هذا الدرسJWT_SECRETنيابةً عنك. - أضف
POST /token. يستقبل المحتوى نفسه الذي يستقبله تسجيل الدخول،{"username": "ada", "password": "correct horse"}، ويتحقق من اسم المستخدم وكلمة المرور بالطريقة نفسها التي يتحقق بهاPOST /login. إذا كانا غير صحيحين، فأرسل استجابة بالحالة 401 و{"error": "Wrong username or password"}. - إذا كانا صحيحين، فوقّع رمزًا وأرسله في الاستجابة على هيئة
{"token": "..."}. يجب أن:- يستخدم الخوارزمية HS256، وأن يُوقّع باستخدام بايتات
JWT_SECRET؛ - يتضمن المطالبة
usernameفي محتواه؛ - يكون معرّف المستخدم
_idنصًا في حقل الموضوع. استخدمtoHexString()، لأنjoseيرفض موضوعًا ليس نصًا؛ - تنتهي صلاحيته بعد ساعة من إنشائه.
يبدو الرمز من هذا الدرس هكذا:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkYSIsInN1YiI6IjY2ZjFjMmE5ZTRiMGExYjJjM2Q0ZTVmNiIsImlhdCI6MTc5MzM2MTYwMCwiZXhwIjoxNzkzMzY1MjAwfQ.EdCrjAKSjZowcjE3-IjKttBLVK1VKKbL9DPnJDLdgug - يستخدم الخوارزمية HS256، وأن يُوقّع باستخدام بايتات
جرّب ذلك: اضغط «تشغيل». أرسل POST /signup مع {"username": "ada", "password": "correct horse"}. أرسل POST /token مع المحتوى نفسه: ستكون الحالة 200 وستتلقى {"token": "..."}. انسخ الرمز وأرسل GET /inspect?token= متبوعًا به. سترى شيئًا مثل {"username": "ada", "sub": "66f1c2a9e4b0a1b2c3d4e5f6", "iat": 1793361600, "exp": 1793365200}: تكون قيمة exp بعد iat بـ3600 ثانية. أرسل POST /token مع {"username": "ada", "password": "wrong horse"}: ستكون الحالة 401.
جرّب بنفسك
import express from 'express';
import { decodeJwt } from 'jose';
import { users } from './db.js';
import { hashPassword, verifyPassword } from './passwords.js';
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 });
});
// يعرض محتوى الرمز: 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' });
}
});
// صفحة المسؤول. لا يمكن الوثوق بأي مطالبة قبل التحقق من توقيعها.
app.get('/admin', (req, res) => {
res.status(403).json({ error: 'Admins only' });
});
app.listen(3000, () => {
console.log('Vault is running on port 3000');
});
جميع دروس المصادقة والأمان
تدرّب بنفسك: Playground لـ Express