في المراحل المبكرة من تطوير التطبيقات، غالباً ما تكون إدارة هوية المستخدم أمراً بسيطاً. لديك جدول قاعدة بيانات للمستخدمين، وتجزئة كلمة مرور بسيطة، وcookie للجلسة. إنه يعمل بشكل مثالي مع أول 1000 مستخدم. ومع ذلك، عندما ينمو نظامك ليصبح بنية موزعة مع خدمات مصغرة (microservices)، وعملاء للهواتف المحمولة، وتكاملات مع أطراف ثالثة، تتفجر تعقيدات إدارة الهوية. هنا يصبح التمييز بين المصادقة (إثبات هويتك) والتفويض (إثبات ما يمكنك فعله) أمراً حاسماً لاستقرار النظام وأمنه.
عند التوسع، لا يمكنك تحمل أن تقوم كل خدمة مصغرة باستعلام قاعدة بيانات مركزية للتحقق من بيانات الاعتماد. تتراكم زمن الاستجابة (Latency)، ويمكن لنقطة فشل واحدة في مزود الهوية أن تعطل منصتك بأكملها. ولحل هذه المشكلة، يعتمد تصميم الأنظمة الحديثة على الرموز غير المرتبطة بالحالة (stateless tokens)، والتحقق اللامركزي، والفصل الصارم بين اهتمامات النظام.
التحول نحو اللامركزية باستخدام رموز JWT
يعتبر رمز الوصول غير المرتبط بالحالة (Stateless Access Token)، الذي يتم تنفيذه عادةً باستخدام رموز الويب JSON (JWT)، حجر الزاوية في المصادقة القابلة للتوسع. على عكس الجلسات التقليدية التي تتطلب من الخادم تخزين الحالة (غالباً في Redis أو قاعدة بيانات)، يحتوي JWT على جميع معلومات المستخدم والصلاحيات اللازمة، وموقعة رقمياً بواسطة الخادم.
عند تسجيل دخول المستخدم، يصدر مزود الهوية (IdP) رمزاً موقّعاً. يقوم العميل بتخزين هذا الرمز (عادةً في الذاكرة أو في cookie httpOnly) ويتضمنه في رأس Authorization للطلبات اللاحقة. يمكن لأي خدمة مصغرة التحقق من توقيع الرمز باستخدام مفتاح عام دون الاتصال مطلقاً بمزود الهوية أو قاعدة البيانات. يقلل هذا بشكل كبير من زمن الاستجابة ويزيل اختناق قاعدة البيانات.
// مثال: التحقق من رمز JWT في خدمة مصغرة تعمل بـ Node.js
const jwt = require('jsonwebtoken');
function verifyAccessToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) {
return res.status(401).send('Access Denied');
}
try {
// التحقق من التوقيع باستخدام المفتاح العام
const verified = jwt.verify(token, process.env.PUBLIC_KEY);
req.user = verified; // إرفاق مطالبات المستخدم بالطلب
next();
} catch (err) {
res.status(403).send('Invalid Token');
}
}
التفويض الدقيق وتحكم الوصول القائم على الأدوار (RBAC)
بينما تتعامل رموز JWT مع المصادقة بكفاءة، فهي أقل مثالية للتفويض الديناميكي لأنها صعبة الإلغاء دون وقت انتهاء صلاحية قصير. للتفويض على نطاق واسع، نستخدم غالباً تحكم الوصول القائم على الأدوار (RBAC) أو تحكم الوصول القائم على السمات (ABAC) المشفر داخل مطالبات JWT أو يتم جلبه عبر مكالمة API خفيفة الوزن.
فكر في سيناريو تتغير فيه صلاحيات المستخدم. مع الجلسة المرتبطة بالحالة (stateful session)، تقوم بتحديث مخزن الجلسة. أما مع رموز JWT، فيجب أن تعتمد على رموز وصول قصيرة العمر (على سبيل المثال، 15 دقيقة) مقترنة برموز تحديث (refresh tokens). يتم تخزين رمز التحديث بأمان ويُستخدم للحصول على رموز وصول جديدة، مما يتيح لك إلغاء الوصول فوراً عن طريق إدراج رمز التحديث في القائمة السوداء إذا لزم الأمر.
فصل الهوية عن منطق الأعمال
للحصول على قابلية توسع حقيقية، يجب عليك فصل الهوية عن خدمات الأعمال الأساسية الخاصة بك. لا تدمج منطق المصادقة في خدمة الطلبات أو خدمة ملف تعريف المستخدم. بدلاً من ذلك، قم بتوسيط إدارة الهوية في مزود هوية مخصص (مثل Auth0 أو Keycloak أو خدمة مبنية خصيصاً). تتعامل هذه الخدمة مع تسجيل الدخول، وإعادة تعيين كلمة المرور، والمصادقة متعددة العوامل (MFA)، وإصدار الرموز.
تعمل خدمات الأعمال الخاصة بك كخوادم موارد. تقوم فقط بالتحقق من توقيع رمز JWT وانتهاء صلاحيته. إذا كنت بحاجة إلى منطق تفويض أكثر تعقيداً (على سبيل المثال، "هل هذا المستخدم هو مالك هذا المورد المحدد؟")، فقم بتنفيذ نقطة قرار سياسة قصيرة العمر أو استخدم مكتبة مثل OPA (وكيل السياسة المفتوح) التي يمكنها اتخاذ قرارات دقيقة بناءً على مطالبات JWT وبيانات تعريف المورد.
الخاتمة
لا يتعلق توسيع نطاق المصادقة والتفويض فقط باختيار المكتبة الصحيحة؛ بل يتعلق بتصميم نظام يقلل من الرحلات الدائرية إلى قواعد البيانات المركزية بينما يزيد من الأمان. من خلال اعتماد رموز JWT غير المرتبطة بالحالة، وإنفاذ فترات صلاحية قصيرة للرموز، وفصل الهوية عن منطق الأعمال، فإنك تخلق بنية تحتية مرنة يمكنها التعامل مع ملايين المستخدمين دون عناء. تذكر أن الأمان عملية مستمرة، لذا قم بمراجعة سياسات الرموز واستراتيجيات التجديد بانتظام للبقاء في المقدمة أمام التهديدات الناشئة.