OAuth 2.0 وتفويض JWT في TypeScript: للمبتدئين
تطبيقات الويب الحديثة نادرًا ما تستغني عن آليات المصادقة والتفويض. المعياران الأكثر شيوعًا هما OAuth 2.0 (بروتوكول تفويض الوصول) وJWT (رمز JSON للويب، طريقة مضغوطة لنقل البيانات). في هذه المقالة، سنشرح كيفية تنفيذ تفويض OAuth 2.0 و JWT في TypeScript، من خلال إنشاء خادم بسيط لكنه عملي. المحتوى موجه للمطورين المبتدئين الذين لديهم معرفة أساسية بـ TypeScript و Node.js.
ما هو OAuth 2.0 و JWT؟
قبل كتابة الكود، من المهم فهم النظرية.
OAuth 2.0
هو بروتوكول يسمح للمستخدم بمنح تطبيق طرف ثالث وصولًا محدودًا إلى موارده (مثل الملف الشخصي على GitHub أو Google) دون مشاركة اسم المستخدم وكلمة المرور. في OAuth 2.0، هناك عدة أدوار:
- مالك المورد — مالك المورد (المستخدم).
- العميل — التطبيق الذي يريد الحصول على الوصول.
- خادم التفويض — الخادم الذي يصدر الرموز بعد المصادقة الناجحة.
- خادم المورد — الخادم الذي يخزن البيانات المحمية.
السيناريو الأكثر شيوعًا هو "تدفق رمز التفويض"، حيث يتم توجيه المستخدم إلى صفحة المزود، ويقوم بتسجيل الدخول، ثم يتبادل الخادم الرمز المستلم مقابل رمز مميز.
JWT (رمز JSON للويب)
JWT هو سلسلة بتنسيق header.payload.signature تحتوي على بيانات (مطالبات) وموقعة بمفتاح سري. يُستخدم كـ رمز وصول (قصير العمر) أو رمز تحديث (طويل العمر) في OAuth 2.0. ميزة JWT هي أنه مكتفي ذاتيًا: يمكن للخادم التحقق منه دون الرجوع إلى قاعدة البيانات.
إعداد المشروع وتثبيت التبعيات
لننشئ مشروعًا جديدًا في TypeScript. تأكد من تثبيت Node.js و npm.
mkdir oauth-jwt-typescriptcd oauth-jwt-typescriptnpm init -ynpm install express jsonwebtoken dotenv cors cookie-parsernpm install -D typescript @types/node @types/express @types/jsonwebtoken @types/cors @types/cookie-parser ts-node nodemonnpx tsc --initفي ملف tsconfig.json، قم بإلغاء تعليق السطر "outDir": "./dist" واضبط "rootDir": "./src". أنشئ هيكل المجلدات:
src/ ├── index.ts ├── auth.ts ├── middleware.ts └── types.ts.envفي ملف .env، حدد:
ACCESS_TOKEN_SECRET=your-secret-key-123REFRESH_TOKEN_SECRET=your-refresh-secret-456PORT=3000تنفيذ الخادم مع OAuth 2.0 و JWT
1. الهيكل الأساسي للخادم (index.ts)
لنكتب خادم Express بسيطًا مع مسارات لتسجيل الدخول، والحصول على الرموز، ونقطة نهاية محمية.
import express from 'express';import cors from 'cors';import cookieParser from 'cookie-parser';import dotenv from 'dotenv';import { login, refresh, logout } from './auth';import { authenticateToken } from './middleware';
dotenv.config();
const app = express();app.use(express.json());app.use(cors());app.use(cookieParser());
// مسار عامapp.get('/', (req, res) => { res.send('OAuth 2.0 + JWT Server is running');});
// المصادقة (محاكاة OAuth 2.0)app.post('/login', login);app.post('/token', refresh); // تحديث رمز الوصولapp.delete('/logout', logout);
// مسار محميapp.get('/protected', authenticateToken, (req, res) => { res.json({ message: 'تم منح الوصول', user: (req as any).user });});
app.listen(process.env.PORT, () => { console.log(`الخادم يعمل على المنفذ ${process.env.PORT}`);});2. أنواع البيانات (types.ts)
export interface TokenPayload { userId: number; username: string;}
export int