تطبيقات سحابية آمنة باستخدام OAuth 2.0 وOpenID Connect
دليل كامل لتأمين تطبيقاتك السحابية باستخدام OAuth 2.0 وOpenID Connect وكيفية تقديم تجربة مستخدم رائعة مع المصادقة والتفويض.
دليل كامل لتأمين تطبيقاتك السحابية باستخدام OAuth 2.0 وOpenID Connect وكيفية تقديم تجربة مستخدم رائعة مع المصادقة والتفويض.
التطبيقات السحابية هي الاتجاه الحالي. على الرغم من أن نوع التطبيق يختلف (ويب، موبايل، سطح المكتب، إلخ)، إلا أنها جميعاً تمتلك خلفية سحابية تقدم خدمات مثل التخزين، الحوسبة، وقواعد البيانات. في معظم الأحيان، تحتاج هذه التطبيقات إلى مصادقة المستخدمين تفويضهم للوصول إلى موارد معينة.
بينما يُمكن بناء آليات مصادقة وتفويض داخلية، أصبحت الأمان واحدة من أهم القضايا عند تطوير التطبيقات السحابية. لحسن الحظ، لدينا معايير مُجرّبة في صناعتنا، مثل OAuth 2.0 وOpenID Connect، لمساعدتنا في تنفيذ المصادقة والتفويض الآمن.
هذه المقالة تفترض الآتي:
نعم، "سمعت" تكفي للنقطة 2 و3. ستستخدم هذه المقالة أمثلة من العالم الحقيقي لشرح المفاهيم وتوضيح العملية بالتصورات التوضيحية. هيا نبدأ!
إذا كنت على دراية بـ OAuth 2.0 وOpenID Connect، يمكنك أيضاً متابعة القراءة لأننا سنغطي بعض الأمثلة الواقعية في هذا القسم؛ إذا كنت جديداً على هذه المعايير، فلا بأس بمتابعتك أيضًا حيث سنقدمها بطريقة بسيطة.
OAuth 2.0 هو إطار عمل للتفويض يسمح للتطبيق بالحصول على وصول محدود إلى موارد محمية على تطبيق آخر بالنيابة عن المستخدم أو التطبيق نفسه. تستخدم معظم الخدمات الشهيرة مثل Google وFacebook وGitHub OAuth 2.0 لتسجيل الدخول الاجتماعي (مثلاً، "تسجيل الدخول باستخدام Google").
على سبيل المثال، لديك تطبيق ويب MyApp يريد الوصول إلى Google Drive للمستخدم. بدلاً من مطالبة المستخدم بمشاركة بيانات اعتماده لدى Google Drive، يمكن لـ MyApp استخدام OAuth 2.0 لطلب الوصول إلى Google Drive نيابةً عن المستخدم. إليك تدفق مبسط:
في هذا التدفق، لا يرى MyApp أبداً بيانات اعتمادات Google Drive للمستخدم. بدلاً من ذلك، يتلقى رمز الوصول من Google الذي يسمح له بالوصول إلى Google Drive نيابةً عن المستخدم.
في مصطلحات OAuth 2.0، MyApp هو العميل، وGoogle هو كل من خادم التفويض وخادم الموارد للبساطة. في العالم الحقيقي، غالباً ما يكون لدينا خادم تفويض وخادم موارد منفصلين لتقديم تجربة تسجيل دخول موحدة (SSO). على سبيل المثال، Google هو خادم التفويض وقد يحتوي على خوادم موارد متعددة مثل Google Drive وGmail وYouTube.
لاحظ أن تدفق التفويض الفعلي أكثر تعقيداً من هذا. يحتوي OAuth 2.0 على أنواع منح مختلفة، ونطاقات، ومفاهيم أخرى يجب أن تكون مدركاً لها. لنضع ذلك جانباً الآن وننتقل إلى OpenID Connect.
OAuth 2.0 رائع للتفويض، ولكن قد تلاحظ أنه لا يحتوي على طريقة للتعريف بالمستخدم (أي المصادقة). OpenID Connect هو طبقة هوية أعلى OAuth 2.0 تضيف قدرات المصادقة.
في المثال أعلاه، يحتاج MyApp إلى معرفة من هو المستخدم قبل بدء تدفق التفويض. لاحظ أن هناك مستخدمان معنيان هنا: مستخدم MyApp ومستخدم Google Drive. في هذه الحالة، يجب أن يعرف MyApp مستخدم تطبيقه الخاص.
لِنرى مثالاً مباشراً، بافتراض أن المستخدمين يمكنهم التسجيل في MyApp باستخدام اسم مستخدم وكلمة مرور:
نظراً لأننا نقوم بمصادقة المستخدم لتطبيقنا الخاص، فعادةً لا تكون هناك حاجة لطلب إذن مثلما فعل Google في تدفق OAuth 2.0. في الوقت نفسه، نحتاج إلى شيء يمكن أن يحدد المستخدم. يقدم OpenID Connect المفاهيم مثل رمز الهوية ونقطة نهاية معلومات المستخدم لمساعدتنا في ذلك.
قد تلاحظ أن موفر الهوية (IdP) هو مشارك جديد ومستقل في التدفق. إنه نفس الشيء مثل خادم التفويض في OAuth 2.0، ولكن لزيادة الوضوح، نستخدم مصطلح IdP لإظهار أنه مسؤول عن مصادقة المستخدم وإدارة الهوية.
عندما ينمو عملك، قد يكون لديك تطبيقات متعددة تشترك في نفس قاعدة بيانات المستخدم. تماماً مثل OAuth 2.0، يسمح لك OpenID Connect بالحصول على خادم تفويض واحد يمكنه مصادقة المستخدمين لأكثر من تطبيق. إذا كان المستخدم مسجلاً بالفعل في تطبيق واحد، فلن يحتاج إلى إدخال بيانات اعتماده مرة أخرى عندما يتم توجيهه إلى IdP بواسطة تطبيق آخر. يمكن إكمال التدفق تلقائياً دون تدخل المستخدم. يُسمى هذا تسجيل الدخول الموحد (SSO).
مرة أخرى، هذا تدفق مبسط للغاية وهناك تفاصيل أكثر تحت السطح. حالياً، لننتقل إلى القسم التالي لمنع الحمل الزائد للمعلومات.
في القسم السابق، استخدمنا تطبيقات الويب كمثال في حين أن العالم أكثر تنوعاً من ذلك. بالنسبة لموفر الهوية، لا يهم حقاً اللغة البرمجية أو الإطار أو المنصة التي تستخدمها. في الممارسة، الاختلاف الملحوظ هو ما إذا كان التطبيق عميلًا عامًا أو عميلًا خاصًا (موثوقًا):
مع وضع ذلك في الاعتبار، لنرى كيف يمكن استخدام OAuth 2.0 وOpenID Connect في أنواع تطبيقات مختلفة.
يمكن استخدام "التطبيق" و"العميل" بالتبادل في سياق هذه المقالة.
التطبيق يعمل على الخادم ويقدم صفحات HTML للمستخدمين. العديد من أطر الويب الشهيرة مثل Express.js، Django، وRuby on Rails تقع في هذه الفئة؛ وأطر العمل للواجهة الخلفية للواجهة مثل Next.js وNuxt.js مشمولة أيضًا. هذه التطبيقات لها الخصائص التالية:
في الوقت نفسه، قد يحتاج التطبيق إلى الوصول إلى خدمات API الداخلية الأخرى في بنية الخدمات المصغرة. أو يمكن أن يكون تطبيقًا متكاملًا يحتاج إلى التحكم في الوصول لأجزاء مختلفة من التطبيق. سنتناول هذا في قسم "حماية واجهة API الخاصة بك".
التطبيق يعمل في متصفح المستخدم ويتواصل مع الخادم من خلال واجهات API. React وAngular وVue.js هي أطر عمل شائعة لبناء التطبيقات الفردية. هذه التطبيقات لها الخصائص التالية:
عادةً ما تحتاج التطبيقات الفردية الصفحة إلى الوصول إلى خدمات API الأخرى لجلب البيانات وتحديثها. سنتناول هذا في قسم "حماية واجهة API الخاصة بك".
التطبيق يعمل على جهاز محمول (iOS، Android، إلخ) ويتواصل مع الخادم من خلال واجهات API. في معظم الحالات، تكون لهذه التطبيقات نفس خصائص التطبيقات الفردية الصفحة.
التطبيقات الماكينات إلى ماكينات هي عملاء تعمل على خادم (ماكينة) وتتواصل مع خوادم أخرى. هذه التطبيقات لها الخصائص التالية:
عند الوصول إلى خدمات أخرى، قد يحتاج التطبيق إلى تقديم رمز الوصول في ترويسة الطلب. سنتناول هذا في قسم "حماية واجهة API الخاصة بك".
التطبيق يعمل على جهاز إنترنت الأشياء (مثل أجهزة المنزل الذكية، الأجهزة القابلة للارتداء، إلخ) ويتواصل مع الخادم من خلال واجهات API. هذه التطبيقات لها الخصائص التالية:
عند التواصل مع الخادم، قد يحتاج الجهاز إلى تقديم رمز الوصول في ترويسة الطلب. سنتناول هذا في قسم "حماية واجهة API الخاصة بك".
مع OpenID Connect، يمكن تحديد هوية المستخدم وجلب بيانات خاصة بالمستخدم من خلال رموز الهوية أو نقاط نهاية معلومات المستخدم. تُسمى هذه العملية بالمصادقة. ولكنك ربما لا تريد أن تعرض جميع مواردك لجميع المستخدمين المُصادقين، على سبيل المثال، يمكن للمسؤولين فقط الوصول إلى صفحة إدارة المستخدم.
هنا يأتي دور التفويض. تذكر أن OAuth 2.0 هو إطار عمل للتفويض، وOpenID Connect هو طبقة هوية أعلى OAuth 2.0؛ مما يعني أنه يمكنك أيضاً استخدام OAuth 2.0 عندما يكون OpenID Connect موجودًا بالفعل.
دعونا نعيد مثالنا الذي استخدمناه في قسم "OAuth 2.0": يريد MyApp الوصول إلى Google Drive للمستخدم. ليس من العملي أن نسمح لـ MyApp بالوصول إلى جميع ملفات المستخدم في Google Drive. بدلاً من ذلك، يجب أن يطلب MyApp بوضوح ما الذي يريد الوصول إليه (مثل الوصول للقراءة فقط إلى ملفات في مجلد معين). يُطلق على ذلك في مصطلحات OAuth 2.0 النطاق.
قد ترى مصطلح "إذن" يُستخدم بالتبادل مع "نطاق" في سياق OAuth 2.0 حيث يكون "النطاق" في بعض الأحيان غامضًا للمستخدمين غير التقنيين.
عندما يمنح المستخدم حق الوصول إلى MyApp، يصدر خادم التفويض رمز وصول بالنطاق المطلوب. يتم بعد ذلك إرسال رمز الوصول إلى خادم الموارد (Google Drive) للوصول إلى ملفات المستخدم.
من الطبيعي، يمكننا استبدال Google Drive بخدمات واجهة API الخاصة بنا. على سبيل المثال، يحتاج MyApp إلى الوصول إلى OrderService لجلب تاريخ طلبات المستخدم. هذه المرة، بما أن مصادقة المستخدم تمت بالفعل بواسطة موفر الهوية وكلا من MyApp وOrderService تحت سيطرتنا، يمكننا تخطي سؤال المستخدم لمنح الوصول؛ يمكن لـ MyApp إرسال الطلب مباشرةً إلى OrderService برمز الوصول الصادر عن موفر الهوية.
قد يحتوي رمز الوصول على نطاق read:order للإشارة إلى أن المستخدم يمكنه قراءة تاريخ طلباته.
الآن، لنقل أن المستخدم يدخل عن طريق الخطأ رابط صفحة الإدارة في المتصفح. نظراً لأن المستخدم ليس مسؤولاً، لا يوجد نطاق admin في رمز الوصول. سيرفض OrderService الطلب ويعيد رسالة خطأ.
في هذه الحالة، قد يعيد OrderService كود الحالة 403 Forbidden للإشارة إلى أن المستخدم غير مصرح له بالوصول إلى صفحة الإدارة.
بالنسبة لتطبيقات ماكينات إلى ماكينات (M2M)، لا يكون المستخدم مشتركاً في العملية. يمكن للتطبيقات طلب رموز الوصول مباشرةً من موفر الهوية واستخدامها للوصول إلى خدمات أخرى. نفس المفهوم ينطبق: يحتوي رمز الوصول على النطاقات الضرورية للوصول إلى الموارد.
يمكننا أن نرى شيئين مهمين يجب مراعاتهما عند تصميم التفويض لحماية خدمات واجهة API الخاصة بك:
read:order, write:order) أو أكثر عمومية (مثل order) بناءً على متطلباتك.admin.فيما يخص التحكم في الوصول، بعض الأساليب الشائعة هي:
من الجدير بالذكر أنه بالنسبة للنهجين، الطريقة القياسية للتحقق من التحكم في الوصول هي فحص نطاقات رمز الوصول، بدلاً من الأدوار أو السمات. الأدوار والسمات يمكن أن تكون ديناميكية جداً والنطاقات أكثر ثباتًا مما يجعلها أسهل في الإدارة بكثير.
لمزيد من التفاصيل حول التحكم في الوصول، يمكنك الرجوع إلى RBAC وABAC: نماذج التحكم في الوصول التي يجب أن تعرفها.
على الرغم من أننا قد ذكرنا مصطلح "رمز الوصول" عدة مرات، إلا أننا لم نناقش كيفية الحصول عليه. في OAuth 2.0، يُصدر رمز الوصول من خادم التفويض (موفر الهوية) بعد تدفق تفويض ناجح.
لِنلقي نظرة أقرب إلى مثال Google Drive ونفترض أننا نستخدم تدفق رمز التفويض:
هناك بعض الخطوات الهامة في التدفق لإصدار رمز الوصول:
يفترض المثال أعلاه أن خادم التفويض (موفر الهوية) وخادم الموارد هما نفس الشيء (Google). إذا كانا منفصلين، بالنظر إلى مثال MyApp وOrderService، سيبدو التدفق مثل هذا:
في هذا التدفق، يصدر خادم التفويض (IdP) كلا من رمز الهوية ورمز الوصول إلى MyApp (الخطوة 8). يُستخدم رمز الهوية لتحديد المستخدم، ويُستخدم رمز الوصول للوصول إلى خدمات أخرى مثل OrderService. بما أن MyApp وOrderService هما خدمات طرف أول، فعادةً لا يطلبان من المستخدم منح الوصول؛ بل يعتمدون على التحكم في الوصول في موفر الهوية لتحديد ما إذا كان يمكن للمستخدم الوصول إلى الموارد (أي ما إذا كان رمز الوصول يحتوي على النطاقات الضرورية).
أخيراً، لنرى كيف يتم استخدام رمز الوصول في تطبيقات ماكينات إلى ماكينات (M2M). نظراً لأنه لا يوجد مستخدم مشارك في العملية والتطبيق موثوق به، يمكنه طلب رمز الوصول مباشرة من موفر الهوية:
يمكن هنا تطبيق التحكم في الوصول. بالنسبة لـ OrderService، لا يهم من هو المستخدم أو أي تطبيق يطلب البيانات؛ يهمه فقط رمز الوصول والنطاقات التي يحتوي عليها.
رموز الوصول عادة ما تكون مشفرة كرموز ويب صغيرة (JWT). لمعرفة المزيد حول JWT، يمكنك الرجوع إلى ما هو رمز الويب JSON (JWT).
يقدم OAuth 2.0 مفهوم النطاقات للسيطرة على الوصول. ومع ذلك، قد تدرك بسرعة أن النطاقات ليست كافية:
openid, offline_access و profile. قد يكون مزج هذه النطاقات القياسية مع النطاقات المخصصة الخاصة بك مربكًا.أحد الحلول الشائعة هو إضافة لاحقات (أو بادئات) إلى أسماء النطاقات للإشارة إلى المورد (خدمة API). على سبيل المثال، read:order و read:product أكثر وضوحًا من read و read. يقدم توسيع OAuth 2.0 RFC 8707 معلمة جديدة resource للإشارة إلى خادم المورد الذي يريد العميل الوصول إليه.
في الواقع، عادة ما تعرف خدمات API بواسطة عناوين URL، لذا فإن استخدام عناوين URL كمؤشرات للموارد أكثر طبيعية. على سبيل المثال، يمكن تمثيل واجهة API الخاصة بـ OrderService كالآتي:
كما ترى، توفر المعلمة راحة استخدام عناوين الموارد الفعلية في طلبات التفويض ورموز الوصول. من الجدير بالذكر أن RFC 8707 قد لا يتم تطبيقها من قبل جميع موفري الهوية. يجب أن تتحقق من وثائق موفر الهوية لرؤية ما إذا كانت تدعم معلمة resource (يدعمها Logto).
بإيجاز، يمكن استخدام معلمة resource في طلبات التفويض ورموز الوصول للإشارة إلى الموارد التي يريد العميل الوصول إليها.
لا يوجد قيود على إمكانية الوصول لمؤشرات الموارد، أي لا يحتاج مؤشر الموارد إلى أن يكون عنوان URL حقيقي يشير إلى خدمة API. لذلك فإن الاسم "مؤشر المورد" يعكس دوره في عملية التفويض بشكل جيد. يمكنك استخدام عناوين URL افتراضية لتمثيل الموارد التي ترغب في حمايتها. على سبيل المثال، يمكن أن تعرف عنوان URL افتراضي
https://api.example.com/adminفي تطبيقك الشامل لتمثيل الموارد التي يمكن فقط للمسؤولين الوصول إليها.
في هذه المرحلة، لقد غطينا أساسيات OAuth 2.0 وOpenID Connect، وكيفية استخدامها في أنواع تطبيقات وسيناريوهات مختلفة. على الرغم أننا قد ناقشناها بشكل منفصل، يمكنك دمجها وفقًا لمتطلبات عملك. يمكن أن تكون البنية العامة بسيطة مثل هذا:
أو أكثر تعقيدا قليلاً:
مع نمو تطبيقك، سترى أن موفر الهوية (IdP) يلعب دوراً محورياً في البنية. لكنه لا يتعلق بأهداف عملك بشكل مباشر. في حين أنه فكرة جيدة أن تُحمله لمزود موثوق، علينا اختيار موفر الهوية بحكمة. يمكن لموفر الهوية الجيد أن يبسط العملية بشكل كبير، يقلل من الجهد التطويري، وينقذك من مآزق الأمان المحتملة.
لتطبيقات السحابة الحديثة، موفر الهوية (أو خادم التفويض) هو المكان المركزي لمصادقة المستخدم، إدارة الهوية، والتحكم في الوصول. بينما ناقشنا العديد من المفاهيم في هذه المقالة، لا يزال هناك العديد من الفروق الدقيقة التي يجب مراعاتها عند تنفيذ مثل هذا النظام. إذا كنت مهتمًا بالتعلم أكثر، يمكنك تصفح مدونتنا للحصول على مقالات أكثر تعمقًا.