مقدمة: لماذا OAuth 2.0 و JWT هما معيار التفويض الحديث
\nفي تطوير الويب الحديث، تعتبر المصادقة والتفويض الآمنان مكونين حاسمين لأي تطبيق. أصبح OAuth 2.0 و JWT (JSON Web Tokens) المعيار الفعلي لبناء الأنظمة الموزعة. يوفر OAuth 2.0 بروتوكول وصول مفوض، بينما يوفر JWT تنسيق رمز مضغوط ومكتفي ذاتيًا.
\nفي هذه المقالة، سنشرح من خلال أمثلة عملية بلغة TypeScript كيفية تنفيذ دورة كاملة للتفويض: من إصدار الرموز إلى التحقق منها وتحديثها. ستتعلم كيفية حماية نقاط نهاية API، وتنفيذ رموز التحديث (refresh tokens)، ومعالجة الأخطاء النموذجية.
\n\n1. الإعداد الأساسي للمشروع وتثبيت التبعيات
\nللبدء، سننشئ مشروعًا على Node.js مع TypeScript. تأكد من تثبيت Node.js (الإصدار 18+) و npm.
mkdir oauth-jwt-typescript\ncd oauth-jwt-typescript\nnpm init -y\nnpm install express cors dotenv jsonwebtoken bcrypt\nnpm install -D typescript @types/express @types/cors @types/jsonwebtoken @types/bcrypt ts-node nodemon\nnpx tsc --init\nسنقوم بإعداد tsconfig.json للعمل مع وحدات ES والأنواع الصارمة. في ملف .env، سنضيف المفاتيح السرية:
ACCESS_TOKEN_SECRET=your-super-secret-access-key\nREFRESH_TOKEN_SECRET=your-super-secret-refresh-key\nACCESS_TOKEN_EXPIRES_IN=15m\nREFRESH_TOKEN_EXPIRES_IN=7d\n\n2. تنفيذ إنشاء رموز JWT والتحقق منها
\nسننشئ أداة مساعدة src/utils/jwt.ts للتعامل مع الرموز. سنستخدم نوعين من الرموز: Access Token (قصير العمر) و Refresh Token (طويل العمر).
import jwt from 'jsonwebtoken';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\ninterface TokenPayload {\n userId: string;\n email: string;\n role: 'user' | 'admin';\n}\n\nexport const generateAccessToken = (payload: TokenPayload): string => {\n return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET!, {\n expiresIn: process.env.ACCESS_TOKEN_EXPIRES_IN || '15m',\n });\n};\n\nexport const generateRefreshToken = (payload: TokenPayload): string => {\n return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n expiresIn: process.env.REFRESH_TOKEN_EXPIRES_IN || '7d',\n });\n};\n\nexport const verifyAccessToken = (token: string): TokenPayload => {\n return jwt.verify(token, process.env.ACCESS_TOKEN_SECRET!) as TokenPayload;\n};\n\nexport const verifyRefreshToken = (token: string): TokenPayload => {\n return jwt.verify(token, process.env.REFRESH_TOKEN_SECRET!) as TokenPayload;\n};\nلاحظ: نحن نستخدم كتابة صارمة للـ payload. وهذا يضمن أن الرمز سيحتوي دائمًا على الحقول المطلوبة.
\n\n3. إنشاء middleware لحماية المسارات
\nالآن سنقوم بتنفيذ middleware الذي سيتحقق من وجود وصلاحية Access Token في رأس Authorization. سننشئ src/middleware/auth.ts:
import { Request, Response, NextFunction } from 'express';\nimport { verifyAccessToken } from '../utils/jwt';\n\nexport interface AuthRequest extends Request {\n user?: {\n userId: string;\n email: string;\n role: 'user' | 'admin';\n };\n}\n\nexport const authenticate = (req: AuthRequest, res: Response, next: NextFunction): void => {\n const authHeader = req.headers['authorization'];\n const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN\n\n if (!token) {\n res.status(401).json({ error: 'Access token is required' });\n return;\n }\n\n try {\n const decoded = verifyAccessToken(token);\n req.user = decoded;\n next();\n } catch (error) {\n res.status(403).json({ error: 'Invalid or expired access token' });\n }\n};\n\nexport const authorizeRole = (...roles: string[]) => {\n return (req: AuthRequest, res: Response, next: NextFunction): void => {\n if (!req.user || !roles