سجل التعديلات (2 تحديثات، آخر تحديث 2 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621616)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
غو كومورا (2026). دمج مصادقة Entra ID في تطبيقات WinForms/WPF ── التشكيل العملي لـ MSAL.NET ووسيط WAM. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621616 https://comcomponent.com/ar/blog/winforms-wpf-entra-id-auth/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621616
- DOI (هذه النسخة)
- 10.5281/zenodo.22241002
«نصنع شاشة تسجيل دخول لكلّ تطبيق أعمال داخليّ، وندير كلمات المرور في قاعدة بياناتنا الخاصّة. وفي كلّ مرّة يغادر فيها موظّف، نتعب من إيقاف حسابه في كلّ تطبيق على حدة» أو «لدينا Microsoft 365 مفعَّل على مستوى الشركة كلّها، أفلا يمكن تسجيل الدخول مباشرةً بنفس ذلك الحساب؟». هذا موضوع يتزايد بثبات في السنوات الأخيرة ضمن استشارات تعديل تطبيقات سطح المكتب. وأحياناً يأتي على شكل طلب من قسم نظم المعلومات بـ«التوقّف عن إدارة كلمات المرور ذاتيّاً»، في سياق حوادث تسرّب كلمات المرور أو التوجّه نحو الثقة الصفريّة (Zero Trust).
بدايةً بالخلاصة: بالنسبة إلى المؤسّسات التي تستخدم Microsoft 365، فإنّ توجيه تسجيل الدخول في تطبيقات WinForms / WPF الداخليّة نحو Entra ID (المعروف سابقاً بـ Azure AD) استثمار سليم التوجّه. فلن يعود التطبيق يحتفظ بكلمات المرور مطلقاً، وتسري دفاعات جانب المستأجر ومراجعاته — كالمصادقة متعدّدة العوامل والوصول المشروط وسجلّات تسجيل الدخول — على التطبيقات الداخليّة مباشرةً. والتنفيذ أيضاً يقتصر على مكتبة MSAL.NET وبضع عشرات من أسطر الشيفرة.
غير أنّ هناك عدداً من المطبّات الخاصّة بتطبيقات سطح المكتب. فالبناء التقليديّ الذي «يستقبل اسم المستخدم وكلمة المرور عبر مربّعات نصّ ويتحقّق منهما خلف الكواليس» (ROPC) في طريقه رسميّاً إلى الإلغاء، ولا يجوز اعتماده في تطوير جديد. وما لم تُستمرّ ذاكرة تخزين الرموز، تظهر شاشة تسجيل الدخول في كلّ مرّة يُشغَّل فيها التطبيق، كما أنّ استخدام الوسيط (WAM) على Windows من عدمه يُحدث فرقاً كبيراً في التجربة والأمان. يجمع هذا المقال، بالترتيب، تنظيماً موجزاً للمفاهيم، وتسجيل التطبيق، وتنفيذ MSAL.NET، وWAM، وذاكرة التخزين، وقرار التبنّي، ومطبّات التشغيل.
جمهور هذه المقالة وبيئة العمل المفترضة
| البند | المحتوى |
|---|---|
| الجمهور | مطوّر يدمج تسجيل الدخول بحساب Microsoft 365 في تطبيق WinForms / WPF قائم أو جديد للأعمال |
| افتراض جانب المستأجر | أن يكون Microsoft 365 / Entra ID معتمَداً. تسجيل التطبيق وموافقة المسؤول (الفصل 3) عمل جانب المستأجر، فإن تعذّر عليك التشغيل بنفسك فاستخدم الفصل 3 كما هو طلباً موجَّهاً إلى قسم نظم المعلومات |
| بيئة التشغيل | Windows. لاستخدام وسيط WAM (الفصل 5) يلزم Windows 10 (1703) فما بعد / Windows Server 2019 فما بعد. وما قبلهما أو Mac وLinux يسقط تلقائيّاً إلى المتصفّح1 |
| .NET | .NET Framework 4.6.2 فما بعد، أو .NET 6 فما بعد. المتصفّح الافتراضيّ في المصادقة التفاعليّة يختلف حسب الإطار (الفصل 4)2 |
| حزم NuGet | Microsoft.Identity.Client (إلزاميّ)، Microsoft.Identity.Client.Broker (لوسيط WAM. MSAL.NET 4.52.0 فما بعد)1، Microsoft.Identity.Client.Extensions.Msal (استمرار ذاكرة تخزين الرموز)3 |
| الشبكة | يلزم الوصول إلى Entra ID عند أوّل تسجيل دخول وعند تحديث الرمز. لا يستقيم الأمر في بيئة بلا اتّصال بالكامل (الفصل 8) |
المختصرات المستخدمة في هذه المقالة
| المختصر | الاسم الكامل | معناه هنا |
|---|---|---|
| MSAL | Microsoft Authentication Library | مكتبة المصادقة التي تقدّمها Microsoft. إصدار .NET هو MSAL.NET (حزمة NuGet Microsoft.Identity.Client) |
| SSO | Single Sign-On | حالة يكفي فيها تسجيل الدخول مرّة واحدة فلا تحتاج إلى إعادة تسجيل الدخول في تطبيق آخر |
| MFA | Multi-Factor Authentication | مصادقة تطلب، إضافة إلى كلمة المرور، عنصراً آخر مثل تطبيق على الهاتف أو المقاييس الحيويّة |
| FIDO | Fast IDentity Online | معيار مصادقة بلا كلمة مرور. في هذه المقالة يشير إلى مفاتيح أمنيّة من نوع USB وما شابه |
| JWT | JSON Web Token | شكل رمز يحمل ادّعاءات (صفات المستخدم) في JSON موقَّع. وهو جسم رمز الهوية ورمز الوصول |
| ROPC | Resource Owner Password Credentials | تدفّق يستقبل فيه التطبيق اسم المستخدم وكلمة المرور مباشرةً للمصادقة. في طريق الإلغاء (القسم 2.3) |
| WAM | Web Account Manager | وسيط المصادقة المضمَّن في Windows (الفصل 5) |
| UPN | User Principal Name | اسم تسجيل الدخول بصيغة taro@example.co.jp. قد يتغيّر عند تغيير اللقب مثلاً (القسم 7.1) |
1. الخلاصة أوّلاً
- عند التخلّي عن الإدارة الذاتيّة للمعرّفات وكلمات المرور والتوجّه نحو Entra ID، يصبح حفظ كلمات المرور، ومعالجة إعادة التعيين، وإيقاف حسابات المغادرين، ومراجعة سجلّات تسجيل الدخول، كلّها من مهام المستأجر بالكامل. وأكبر فائدة هي التقلّص الهائل في نطاق مسؤوليّة التطبيق.
- تطبيق سطح المكتب عميل عامّ (public client). ولأنّ ملفّ exe يمكن تحليله لدى جهة التوزيع، فلا يمكنه (ولا يجوز له) أن يحمل سرّ عميل. ويُشكَّل تسجيل التطبيق بدوره بوصفه عميلاً عامّاً.4
- ROPC (Resource Owner Password Credentials)، الذي يحتفظ فيه التطبيق مباشرةً باسم المستخدم وكلمة المرور، مذكور رسميّاً على أنّه «أُلغِيَ (deprecated)»، وقد صدر دليل للانتقال منه. وهو لا يتوافق مع المصادقة متعدّدة العوامل والوصول المشروط، وهو في طريقه عمليّاً إلى التعطّل عن الاستخدام. اعتبر أنّ اعتماده في تطوير جديد ممنوع.56
- التنفيذ يتمّ عبر MSAL.NET (Microsoft.Identity.Client)، ونمط الاستدعاء الأساسيّ الوحيد هو: استدعِ
AcquireTokenSilentأوّلاً دائماً، وإن ظهرMsalUiRequiredExceptionفاستدعِAcquireTokenInteractive.7 - على Windows، يُوصى بالمصادقة عبر وسيط WAM (Web Account Manager). وبسطر واحد من
WithBrokerتحصل على SSO مع الحساب المسجَّل دخوله بالفعل في Windows، ودعم للوصول المشروط وWindows Hello ومفاتيح FIDO، وربط رمز التحديث بالجهاز.1 - إن نسيتَ جعل ذاكرة تخزين الرموز مستمرّة، ستظهر شاشة تسجيل الدخول في كلّ مرّة يُعاد فيها تشغيل التطبيق. أدرِج الذاكرة المؤقّتة المشفَّرة من
Microsoft.Identity.Client.Extensions.Msalمنذ البداية.3 - مصادقة Entra آليّة تفترض وجود شبكة. فهي لا تصلح للتطبيقات الميدانيّة التي يجب أن تعمل بلا اتّصال بالكامل، لذا ابدأ بتحديد إمكان التبنّي من عدمه عبر جدول القرار في الفصل 8.
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 25، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. الصورة العامّة ── ماذا يعني التوقّف عن إدارة كلمات المرور ذاتيّاً
2.1 ما مشكلة الإدارة الذاتيّة
عندما يدير تطبيق الأعمال كلمات المرور في جدول مستخدمين خاصّ به، تقع كلّ المسؤوليّات التالية على عاتق التطبيق (أي علينا نحن المطوّرين).
- الحفظ: اختيار طريقة التجزئة (hash) وتنفيذها (ما زلنا نرى جداول عمرها 15 عاماً تُركت على MD5 بلا ملح)
- التشغيل: التعامل مع استفسارات إعادة تعيين كلمة المرور، والحظر بعد محاولات فاشلة، وتوزيع كلمات المرور الأوّليّة
- دورة الحياة: إيقاف الحساب عند المغادرة أو النقل. فإن وُجدت 5 تطبيقات، يلزم إيقافه 5 مرّات
- المراجعة: تسجيل وحفظ من سجّل الدخول ومتى. والمصادقة متعدّدة العوامل غير قابلة للتنفيذ عمليّاً
عند تفويض المصادقة إلى Entra ID، تختفي هذه العناصر الأربعة من شيفرة التطبيق وتتوحّد في إدارة المستأجر. ومن يغادر العمل يكفي تعطيل حسابه في Entra ID ليصبح عاجزاً فوراً عن تسجيل الدخول في جميع التطبيقات، وتُحفَظ سجلّات تسجيل الدخول تلقائيّاً أيضاً. لا يكاد يوجد سبب يدعو مؤسّسة اعتمدت Microsoft 365 بالفعل إلى الاستمرار في مصادقة ذاتيّة الصنع. أمّا الآليّة المقابلة لتوجيه تسجيل الدخول إلى Windows نفسه نحو حساب Google في مؤسّسات Google Workspace، فقد تناولناها في «ما هو GCPW».
2.2 الحدّ الأدنى من المفاهيم ── العميل العامّ والرموز
نتجاوز الشرح المدرسيّ لـ OAuth 2.0 / OpenID Connect، ونكتفي بسرد المفاهيم اللازمة فقط لتنفيذ تطبيق سطح المكتب.
| المفهوم | معناه في تطبيق سطح المكتب |
|---|---|
| العميل العامّ (public client) | تطبيقات مثل ملفّات exe والتطبيقات المحمولة، لا تستطيع الاحتفاظ بسرّ (سرّ العميل) بأمان. تستطيع الحصول على الرموز نيابةً عن المستخدم فقط |
| العميل السرّيّ (confidential client) | تطبيقات مثل خواديم الويب والعفاريت (daemons)، تستطيع الاحتفاظ بسرّ أو شهادة. تطبيق سطح المكتب ليس من هذا النوع |
| رمز الهوية (ID token) | JWT يمثّل «من هذا الشخص». يكفي هذا وحده إن أردتَ وظيفة تسجيل الدخول فقط |
| رمز الوصول (access token) | تصريح مرور لاستدعاء واجهة API معيّنة (كـ Microsoft Graph أو واجهة API خاصّة بالشركة). يحمل الوجهة (audience) والنطاق (scope) المخبوزَين فيه |
| رمز التحديث (refresh token) | رمز لتحديث الرمزين أعلاه دون تفاعل. يُدار تلقائيّاً داخل ذاكرة التخزين الخاصّة بـ MSAL، ولا يظهر مباشرةً للتطبيق |
المهمّ هو السطر الأوّل. فبما أنّ ملفّ exe يمكن تحليله وإعادة تجميعه لدى جهة التوزيع، فإنّ أيّ «سرّ» يُضمَّن فيه لا يبقى سرّاً. لذا يُسجَّل التطبيق كعميل عامّ يعمل دون سرّ، ويُصمَّم بحيث يُترَك جوهر المصادقة نفسه (إدخال كلمة المرور والمصادقة متعدّدة العوامل) للمتصفّح أو وسيط نظام التشغيل، بينما لا يستقبل التطبيق سوى الرموز. وكون التطبيق لا يلمس كلمة مرور المستخدم إطلاقاً هو جوهر هذه الآليّة بالذات.
2.3 ROPC طريقة انتهى عصرها ── نتيجة التحقّق من المصادر الرسميّة
الفكرة التقليديّة تميل إلى القول: «يكفي أن نستقبل اسم المستخدم وكلمة المرور عبر شاشة تسجيل دخول خاصّة بنا، ثمّ نطلب من Entra ID التحقّق منهما خلف الكواليس». هذا هو ROPC (تمرير اسم المستخدم وكلمة المرور مباشرةً)، وهو لا يزال موجوداً في MSAL.NET باسم AcquireTokenByUsernamePassword، لكنّ الوصف الحاليّ في الوثائق الرسميّة واضح.
- مذكور صراحةً أنّ ROPC الموجَّه للعملاء العامّين «أُلغِيَ (deprecated) بسبب المخاطر الأمنيّة»، وقد نُشر دليل للانتقال إلى تدفّقات أكثر أماناً.6
- ROPC غير متوافق مع المصادقة متعدّدة العوامل والوصول المشروط. فالمستخدمون الذين تُلزمهم سياسة المستأجر بالمصادقة متعدّدة العوامل يُحظَرون ولا يستطيعون تسجيل الدخول عبر هذا التدفّق.5
- لا يعمل SSO، ولا يمكن استخدام حسابات Microsoft الشخصيّة، ولا يمكن للحسابات الخالية من كلمة المرور (FIDO أو Authenticator) تسجيل الدخول أيضاً.5
- كما يتّجه جانب واجهات API الخاصّة بـ Microsoft للويب نحو قبول الرموز التي خضعت للمصادقة متعدّدة العوامل فقط، وتكتب الوثائق الرسميّة نفسها: «التطبيقات المعتمِدة على ROPC ستُستبعَد (locked out). على تطبيقات سطح المكتب الانتقال إلى مصادقة قائمة على الوسيط».5
بما أنّ إلزام المصادقة متعدّدة العوامل قد يحدث في أيّ وقت عبر إعداد جانب المستأجر، فإنّ اعتماد ROPC بحجّة «أنّه يعمل الآن» يعرّضك لخطر أن يعجز جميع المستخدمين فجأة يوماً ما عن تسجيل الدخول. وإن كان تطبيق قائم يعمل بالفعل عبر ROPC، فضع خطّة على أساس الانتقال منه. طريقتا الحصول على الرمز المسموح استخدامهما فعليّاً في تطبيقات سطح المكتب هما التاليتان فقط.
| التدفّق | مكان الاستخدام |
|---|---|
| التفاعليّ (وسيط / متصفّح) | تطبيق GUI عاديّ. الخيار الأساسيّ |
| تدفّق رمز الجهاز (device code flow) | بيئات لا يمكن فيها عرض متصفّح (كطرفيّة SSH البعيدة مثلاً). يعرض عنوان URL ورمزاً، ويُطلَب من المستخدم تسجيل الدخول عبر متصفّح على جهاز آخر |
3. تسجيل التطبيق ── الإعداد في مركز إدارة Entra
قبل كتابة أيّ شيفرة، سجِّل التطبيق في المستأجر. وإن تعذّر على المطوّر القيام بذلك بنفسه، فاستخدم محتوى هذا القسم كما هو ليكون طلباً موجَّهاً إلى قسم نظم المعلومات.
قد يتغيّر مظهر قوائم مركز الإدارة، لذا نكتب هنا باسم الشاشة التي تصل إليها، وبما تضغط عليه في تلك الشاشة، لا بمظهر القائمة. نعرض العمل كلّه أوّلاً في جدول.
| الغرض | الشاشة التي تفتحها | ما تفعله في تلك الشاشة |
|---|---|---|
| تسجيل التطبيق | «تسجيل التطبيقات» (App registrations) | «تسجيل جديد» → حدّد الاسم و«أنواع الحسابات المدعومة» (القسم 3.1) |
| تدوين المعرّفات | «نظرة عامّة» للتطبيق المسجَّل | انسخ «معرّف التطبيق (العميل)» و«معرّف الدليل (المستأجر)» |
| إضافة URI إعادة التوجيه | «المصادقة» للتطبيق المسجَّل | «إضافة منصّة» → «تطبيقات الجوّال وسطح المكتب» → أدخل URI (الثلاثة في جدول القسم 3.2) |
| إضافة أذونات الوصول | «أذونات API» للتطبيق المسجَّل | «إضافة إذن» → «Microsoft Graph» → «أذونات مفوَّضة» → User.Read (القسم 3.3) |
| منح موافقة المسؤول | «أذونات API» للتطبيق المسجَّل | نفِّذ «منح موافقة المسؤول لـ (اسم المستأجر)» (القسم 3.3) |
| السماح للعميل العامّ | «المصادقة» للتطبيق المسجَّل | «السماح بتدفّقات العميل العامّ» في «الإعدادات المتقدّمة» (القسم 3.4) |
3.1 التسجيل نفسه
أنشئ التسجيل من [App registrations] ← [New registration] في مركز إدارة Microsoft Entra (entra.microsoft.com).8
- الاسم: يظهر في شاشة الموافقة وسجلّات تسجيل الدخول، فاختر اسماً مفهوماً لجانب الأعمال مثل «نظام إدارة المخزون».
- أنواع الحسابات المدعومة: بالنسبة إلى تطبيق داخليّ، الخيار الوحيد هو «الحسابات في دليل هذه المؤسّسة فقط» (مستأجر واحد). أمّا تعدّد المستأجرين فللمنتجات التي تُوزَّع على مؤسّسات متعدّدة فقط.
- دوِّن معرّف التطبيق (العميل) ومعرّف الدليل (المستأجر) اللذين يظهران بعد التسجيل، وضمِّنهما في إعدادات التطبيق (لا يُعدّ أيّ منهما معلومة سرّيّة).
3.2 عنوان URI لإعادة التوجيه ── المنصّة هي «تطبيقات الجوّال وسطح المكتب»
هذا إعلان عن المكان الذي يُستقبَل فيه الرمز بعد المصادقة. اختر [Authentication] ← [Add a platform] ← [Mobile and desktop applications]، وسجِّل عنوان URI المناسب لطريقة المصادقة.4
| طريقة المصادقة | عنوان URI لإعادة التوجيه الذي يُسجَّل |
|---|---|
| وسيط WAM (الخيار الأساسيّ، الفصل 5) | ms-appx-web://microsoft.aad.brokerplugin/{معرّف العميل} |
| متصفّح النظام | http://localhost |
| متصفّح مضمَّن | https://login.microsoftonline.com/common/oauth2/nativeclient |
لا يُكتَب ms-appx-web://... الخاصّ بـ WAM في شيفرة MSAL، لكنّه إلزاميّ في جانب تسجيل التطبيق.9 وبالنظر إلى الرجوع الاحتياطيّ إلى المتصفّح في البيئات التي لا يعمل فيها WAM (الفصل 5)، فمن العمليّ تسجيل العناصر الثلاثة في الجدول كلّها منذ البداية. ويجدر الانتباه بوجه خاصّ إلى سلوك WithDefaultRedirectUri()، إذ تعتمد الوجهة التي يُحلّ إليها على المنصّة: في .NET Framework تُحَلّ إلى https://login.microsoftonline.com/common/oauth2/nativeclient، وفي .NET (Core فما بعد) تُحَلّ إلى http://localhost.10 فإن كان تطبيق .NET Framework مسجَّلاً فيه ms-appx-web وhttp://localhost فقط، ورجع احتياطيّاً من WAM إلى المتصفّح، فسيحدث خطأ مصادقة بسبب عدم التطابق مع nativeclient. سجِّل العناصر الثلاثة كلّها، أو ثبِّتها صراحةً عبر WithRedirectUri(...). ومن المطبّات المعتادة أيضاً التسجيل عن طريق الخطأ في منصّة «Web»، ما يسبّب خطأ مصادقة.
3.3 أذونات الوصول إلى API وموافقة المسؤول
في [API permissions] أضف أذونات مفوَّضة (delegated permission) لواجهة API التي يستدعيها التطبيق. لتسجيل الدخول وعرض الملفّ الشخصيّ فقط، يكفي User.Read في Microsoft Graph الممنوح افتراضيّاً.
بعد الإضافة، اطلب تنفيذ [Grant admin consent for (اسم المستأجر)].8 فبذلك تختفي نافذة موافقة كلّ مستخدم عند أوّل تسجيل دخول. وفي المستأجرين الذين عُطِّلت فيهم موافقة المستخدم نفسه، يتوقّف أوّل تسجيل دخول عند رسالة «يلزم موافقة المسؤول» دون موافقة المسؤول، لذا من المبدأ في التطبيقات الموزَّعة داخليّاً إتمام موافقة المسؤول قبل التوزيع.
3.4 علامة «السماح بتدفّقات العميل العامّ»
علامة «السماح بتدفّقات العميل العامّ» (Allow public client flows) الموجودة ضمن الإعدادات المتقدّمة لـ [Authentication] تُضبَط على «نعم» عند استخدام تدفّقات لا تستعمل عنوان URI لإعادة التوجيه، كتدفّق رمز الجهاز أو مصادقة Windows المتكاملة.4 وهي ليست إلزاميّة إن اقتصر الأمر على التدفّق التفاعليّ (متصفّح / وسيط). ومن الجدير بالذكر أنّه لا يُنشَأ في هذا التسجيل لا سرّ العميل ولا شهادة. فبقاء حقل «Certificates & secrets» فارغاً هو الحالة الصحيحة للعميل العامّ (نظراً لكثرة الاستفسارات الناتجة عن الخلط بينهما، نعود إلى هذا في الفصل 9).
4. التنفيذ عبر MSAL.NET ── الشكل الأساسيّ Silent ثم Interactive
نرسم أوّلاً من يفعل ماذا في هذا الشكل الأساسيّ. يكفي أن تقرأ أنّ التطبيق لا يستقبل كلمة المرور ولا مرّة، ويستقبل الرمز فقط.
sequenceDiagram
participant APP as تطبيق سطح المكتب
participant MSAL as MSAL.NET (ذاكرة تخزين الرموز)
participant UI as الوسيط / المتصفّح
participant EID as Entra ID
APP->>MSAL: AcquireTokenSilent
alt يوجد رمز صالح في الذاكرة المؤقّتة
MSAL-->>APP: رمز وصول (لا تظهر شاشة)
else غير موجود، أو يتعذّر التحديث
MSAL-->>APP: MsalUiRequiredException
APP->>MSAL: AcquireTokenInteractive
MSAL->>UI: وجهة الحوار تُحدَّد بالتكوين (الفصل 5)
UI->>EID: تسجيل الدخول (MFA / Windows Hello / FIDO)
EID-->>UI: نتيجة المصادقة
UI-->>MSAL: إعادة النتيجة (ما يُعاد يختلف حسب المسار · الشكل 2)
MSAL-->>APP: رمز وصول
end
الشكل 1: إدخال كلمة المرور يكتمل داخل الوسيط أو المتصفّح، ولا يصل إلى التطبيق سوى الرمز. لذلك لا يحتاج التطبيق إلى سرّ. وما بعد استدعاء API بالرمز المستلم في الفصل 7.
أضف Microsoft.Identity.Client عبر NuGet. يكفي تذكّر نمط تنفيذ واحد فقط: استدعِ AcquireTokenSilent أوّلاً دائماً، وارجع احتياطيّاً إلى التفاعليّ فقط عند استقبال MsalUiRequiredException. وبما أنّ AcquireTokenInteractive مصمَّم بحيث لا ينظر إلى ذاكرة التخزين إطلاقاً، فإنّ استدعاءه مباشرةً يُظهر شاشة تسجيل الدخول في كلّ مرّة.7
using Microsoft.Identity.Client;
public sealed class AuthService
{
private const string ClientId = "アプリケーション(クライアント)ID";
private const string TenantId = "ディレクトリ(テナント)ID";
private static readonly string[] Scopes = { "User.Read" };
private readonly IPublicClientApplication _app;
public AuthService()
{
_app = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority(AzureCloudInstance.AzurePublic, TenantId)
.WithRedirectUri("http://localhost") // システムブラウザー用
.Build();
// 実運用ではここでトークンキャッシュの永続化を登録する(6章)
}
public async Task<AuthenticationResult> SignInAsync(IntPtr ownerHwnd)
{
// 1. キャッシュ済みアカウントでのサイレント取得を必ず先に試す
var accounts = await _app.GetAccountsAsync();
var account = accounts.FirstOrDefault();
try
{
return await _app.AcquireTokenSilent(Scopes, account)
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
// 2. 対話が必要なときだけサインイン画面を出す。
// .NET Framework の既定は旧式の埋め込み WebView のため、
// http://localhost リダイレクト=システムブラウザーを明示する
// (.NET 6+ はもともとシステムブラウザーのみ)
return await _app.AcquireTokenInteractive(Scopes)
.WithAccount(account)
.WithParentActivityOrWindow(ownerHwnd)
.WithUseEmbeddedWebView(false)
.ExecuteAsync();
}
}
}
يمرَّر مقبض النافذة المالكة من جانب الاستدعاء. وذلك لمنع اختفاء نافذة المصادقة خلف التطبيق، وهو إلزاميّ في WAM.1 نقطة أخرى: لا تحذف WithUseEmbeddedWebView(false) في تطبيقات .NET Framework. فـ الوضع الافتراضيّ للمصادقة التفاعليّة في .NET Framework هو WebView مضمَّن، بينما إعادة التوجيه إلى http://localhost مخصّصة لمتصفّح النظام (فإن اختلّ التوافق بينهما، قد يقع الأمر في متصفّح مضمَّن قديم لا تعمل معه المصادقة المشروطة أو Windows Hello / FIDO، أو يحدث عدم تطابق في عنوان إعادة التوجيه).2 ابتداءً من .NET 6 لا يوجد WebView مضمَّن أصلاً، ويُستخدَم متصفّح النظام دائماً، لذا يصبح هذا التحديد زائداً لكن غير ضارّ.
// WinForms (Form のメソッド内)
var result = await _authService.SignInAsync(this.Handle);
// WPF
var hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
var result = await _authService.SignInAsync(hwnd);
this.Text = $"مسجَّل الدخول: {result.Account.Username}";
نضيف بعض النقاط الجديرة بالانتباه.
- استخدم نسخة واحدة من
IPublicClientApplicationفي التطبيق كلّه وأعد استخدامها. فبما أنّ لكلّ نسخة ذاكرة تخزين خاصّة بها، فإنّ استدعاءCreateفي كلّ مرّة يُبطل عمل الحصول الصامت. MsalUiRequiredExceptionليس «حالة شاذّة»، بل تدفّق تحكّم اعتياديّ يعني «يلزم التفاعل». يحدث عند أوّل تشغيل، أو انتهاء صلاحيّة رمز التحديث، أو تغيّر متطلّبات الوصول المشروط، وما شابه.- الاستخدام الصحيح هو استدعاء
AcquireTokenSilentفي كلّ مرّة مباشرةً قبل استدعاء واجهة API. فإن وُجد رمز صالح في ذاكرة التخزين، يُعاد فوراً، وإن اقترب من الانتهاء يُجدَّد تلقائيّاً.7 لا يجوز الاحتفاظ برمز الوصول بنفسك وإدارة مدّة صلاحيّته. - الانتظار عبر
.Resultأو.Wait()في خيط الواجهة يسبّب توقّفاً (deadlock) (راجع «async وخيط الواجهة في WPF/WinForms في صفحة واحدة»).
5. وسيط WAM ── التشكيل الموصى به على Windows
شيفرة الفصل 4 تشكيل يفتح متصفّحاً، لكن على Windows توجد طريقة أفضل بدرجة. WAM (Web Account Manager) وسيط مصادقة مدمَج في Windows 10 (الإصدار 1703 فما بعده) وWindows Server 2019 فما بعده، وتذكر الوثائق الرسميّة له الفوائد الأربع التالية.1
- تعزيز الأمان: يُربَط رمز التحديث بالجهاز، فلا يمكن استخدامه على جهاز آخر حتّى إن سُرِق (حماية الرموز). وتصل تحسينات أمنيّة مستمرّة عبر تحديثات نظام التشغيل.
- دعم الميزات: تصبح ميزات المصادقة المرتبطة بنظام التشغيل والخدمات، مثل Windows Hello والوصول المشروط ومفاتيح FIDO، متاحة دون أيّ شيفرة إضافيّة.
- التكامل مع النظام: يظهر الحساب المسجَّل دخوله بالفعل في Windows في منتقي الحسابات المدمَج، فتكتمل عمليّة تسجيل الدخول في أغلب الأحوال دون إدخال كلمة مرور. وهو SSO فعليّاً.
- حماية الرموز: يمكن التعامل مع سياسات حماية الرموز الخاصّة بالوصول المشروط.
فإن كان الحاسوب الداخليّ منضمّاً إلى Entra (أو Hybrid Join)، تصبح التجربة: «تشغيل التطبيق ← اختيار حساب Windows ← اكتمال تسجيل الدخول فوراً»، دون إدخال كلمة مرور في أيّ مكان.
ما كتبناه في الشكل 1 بأنّ «ما يُعاد يختلف حسب المسار» هو الفرق هنا. الوسيط ليس بديلاً عن المتصفّح، بل فاعل يُنهي الحصول على الرمز بنفسه ثمّ يعيد النتيجة.
flowchart TB
A["AcquireTokenInteractive"] --> Q{"WithBroker مفعَّل و<br/>بيئة WAM متاحة؟"}
Q -->|"متاح"| BR["وسيط WAM<br/>يكتمل الحصول على الرمز في جانب الوسيط،<br/>ويعيد الرمز إلى MSAL"]
Q -->|"غير متاح / بيئة غير مدعومة (الفصل 4)"| BW["متصفّح النظام<br/>يعيد رمز التفويض إلى MSAL،<br/>ويستبدله MSAL برمز من Entra ID"]
BR --> T["يضع MSAL الرمز في الذاكرة المؤقّتة ويعيده إلى التطبيق"]
BW --> T
الشكل 2: إذا تغيّرت وجهة الحوار، تغيّر ما يستقبله MSAL. عبر الوسيط لا يبقى تبديل رمز التفويض في جانب MSAL.
5.1 التنفيذ ── WithBroker والحزمة
لاستخدام WAM يلزم MSAL.NET 4.52.0 فما بعد والحزمة الإضافيّة Microsoft.Identity.Client.Broker.1 أضف WithBroker إلى بانٍ الفصل 4.
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Broker; // لـ WithBroker(BrokerOptions)
var brokerOptions = new BrokerOptions(BrokerOptions.OperatingSystems.Windows)
{
Title = "نظام إدارة المخزون" // العنوان الذي يظهر في منتقي الحسابات
};
_app = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority(AzureCloudInstance.AzurePublic, TenantId)
.WithDefaultRedirectUri()
.WithParentActivityOrWindow(() => _ownerHwnd) // WAMでは必須
.WithBroker(brokerOptions)
.Build();
يمكن تعزيز جانب الحصول الصامت بسطر واحد. عندما لا يوجد حساب في الذاكرة المؤقّتة، تمرير PublicClientApplication.OperatingSystemAccount يجرّب تسجيلاً صامتاً بحساب Windows المسجَّل دخوله الآن. هذا النمط الموصى به رسميّاً يُكمل تسجيل الدخول من أوّل تشغيل بلا حوار.9
var accounts = await _app.GetAccountsAsync();
var account = accounts.FirstOrDefault()
?? PublicClientApplication.OperatingSystemAccount;
try
{
return await _app.AcquireTokenSilent(Scopes, account).ExecuteAsync();
}
catch (MsalUiRequiredException)
{
return await _app.AcquireTokenInteractive(Scopes).ExecuteAsync();
}
ومع ذلك، سجِّل في جانب تسجيل التطبيق، كما في القسم 3.2، ms-appx-web://microsoft.aad.brokerplugin/{معرّف العميل} على منصّة «تطبيقات الجوّال وسطح المكتب».1 إن نسيتَ ذلك يفشل الحوار بخطأ وسيط. نقطة أخرى: WithDefaultRedirectUri() في العيّنة أعلاه يحدّد عنوان إعادة التوجيه عندما يتعذّر WAM فيسقط الأمر إلى المتصفّح، والوجهة تعتمد على المنصّة (.NET Framework → nativeclient، .NET → http://localhost).10 إن سُجِّلت الثلاثة في جدول القسم 3.2 مرّ أيّ منهما، وإن أردت تضييق التسجيل فصرِّح بـ WithRedirectUri(...).
5.2 قيود WAM ── مطبّات إن لم تكن على علم بها
| القيد | المحتوى |
|---|---|
| نظام التشغيل | Windows 10 (1703)+ / Windows Server 2019+. وما قبلهما وMac وLinux يسقط تلقائيّاً إلى المتصفّح1 |
| موفّر الهوية | مخصّص لـ Entra ID. authority لـ Azure AD B2C أو AD FS غير مدعوم (يسقط إلى المتصفّح)1 |
| سياق التنفيذ | يفترض إمكان إظهار واجهة في جلسة مستخدم تفاعليّة. خدمات Windows، وجدولة المهام (خارج جلسة المستخدم)، وتنفيذ مستخدم آخر بـ runas تُصدر خطأً بحسب التصميم1 |
السطر الثالث مهمّ بوجه خاصّ. أن يعمل في تطبيق ذي شاشة ثمّ يفشل عند إعادة استخدام الشيفرة نفسها في دفعة ليليّة هو سلوك بالمواصفات. التشغيل غير المراقب مجال يُفصَل فيه التصميم بصلاحيّات التطبيق (عميل سرّيّ) لا بتفويض المستخدم. وبما أنّ الرجوع الاحتياطيّ مضمَّن في المواصفات، يمكن تحقيق «WAM أوّلاً، فإن تعذّر فالمتصفّح» في شيفرة واحدة، وهذا من محاسن MSAL.
5.3 الشكل النهائيّ ── مثال موحَّد يقدّم الوسيط ويسقط إلى المتصفّح
الفصل 4 تشكيل متصفّح فقط، والقسم 5.1 أضاف الوسيط وحده، فنوحّدهما في شكل يُستخدم في العمل كما هو. أوّلاً نرتّب طريقتَي تحديد عنوان إعادة التوجيه اللتين يسهل الخلط بينهما.
| الكتابة | عنوان إعادة التوجيه المستخدم فعليّاً | موضع الاستخدام |
|---|---|---|
WithRedirectUri("http://localhost") |
دائماً http://localhost (لمتصفّح النظام) |
عندما تريد تثبيته على واحد مسجَّل. من .NET 6 فما بعد يتطابق بهذا |
WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient") |
nativeclient دائماً | عندما تستخدم WebView مضمَّناً في .NET Framework |
WithDefaultRedirectUri() |
يعتمد على المنصّة (.NET Framework → nativeclient، .NET → http://localhost)10 |
عندما سُجِّلت الثلاثة في جدول القسم 3.2 وتريد الشيفرة نفسها بغضّ النظر عن الإطار |
أيّاً اخترت، طالما يعمل WAM لا يظهر هذا العنوان. لا يؤثّر إلا عند السقوط إلى المتصفّح. المثال الموحَّد أدناه يثبّت العنوان صراحةً بـ WithRedirectUri حتّى لا يقع حادث إن ضيّقت التسجيل.
using System.IO;
using System.Linq;
using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Broker; // WithBroker(BrokerOptions) 用
using Microsoft.Identity.Client.Extensions.Msal; // MsalCacheHelper 用
public sealed class AuthService
{
private const string ClientId = "アプリケーション(クライアント)ID";
private const string TenantId = "ディレクトリ(テナント)ID";
private static readonly string[] Scopes = { "User.Read" };
private readonly IPublicClientApplication _app;
private readonly IntPtr _ownerHwnd;
private AuthService(IPublicClientApplication app, IntPtr ownerHwnd)
{
_app = app;
_ownerHwnd = ownerHwnd;
}
// تسجيل الذاكرة المؤقتة غير متزامن، لذا اجعله طريقة مصنع.
// أنشئ مثيلاً واحداً في التطبيق وأعد استخدامه (الفصل 4)
public static async Task<AuthService> CreateAsync(IntPtr ownerHwnd)
{
var brokerOptions = new BrokerOptions(BrokerOptions.OperatingSystems.Windows)
{
Title = "نظام إدارة المخزون" // العنوان الذي يظهر في منتقي الحسابات
};
var app = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority(AzureCloudInstance.AzurePublic, TenantId)
// WAMが使えずブラウザーへ落ちたときのリダイレクトURI。
// 解決先がプラットフォーム依存になる WithDefaultRedirectUri() ではなく、
// アプリ登録済みの値へ明示的に固定する
.WithRedirectUri("http://localhost")
.WithParentActivityOrWindow(() => ownerHwnd) // WAMでは必須
.WithBroker(brokerOptions) // 使えなければ自動でブラウザーへ
.Build();
// トークンキャッシュの永続化(6章)。Build() 直後に1回だけ登録する
var storageProperties = new StorageCreationPropertiesBuilder(
"msal_cache.dat",
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"KomuraSoft", "InventoryApp"))
.Build();
var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties);
cacheHelper.RegisterCache(app.UserTokenCache);
return new AuthService(app, ownerHwnd);
}
// 前回サインインしたアカウントの識別子。アプリの設定に保存し、
// 起動時に読み込んでおく(保存先はアプリ設定・レジストリなど何でもよい)
private string? _homeAccountId;
// APIを呼ぶ直前に毎回これを呼ぶ。アクセストークンを自前で保持しない(4章)
public async Task<AuthenticationResult> AcquireTokenAsync()
{
// 1. どのアカウントで silent を試すかを決める
IAccount? account = await ResolveAccountAsync();
if (account is not null)
{
try
{
return await _app.AcquireTokenSilent(Scopes, account).ExecuteAsync();
}
catch (MsalUiRequiredException)
{
// 対話へ落とす
}
}
// 2. 対話。WAMが使えればアカウントピッカー、
// 使えなければ上で指定したシステムブラウザーが開く
AuthenticationResult result =
await _app.AcquireTokenInteractive(Scopes)
.WithParentActivityOrWindow(_ownerHwnd)
.WithUseEmbeddedWebView(false) // .NET Framework用(4章)
.ExecuteAsync();
// 選ばれた人を覚える。次回はこの人で silent を試す
_homeAccountId = result.Account?.HomeAccountId?.Identifier;
SaveHomeAccountId(_homeAccountId);
return result;
}
private async Task<IAccount?> ResolveAccountAsync()
{
List<IAccount> accounts = (await _app.GetAccountsAsync()).ToList();
// 前回選んだ人がキャッシュに残っていれば、その人
if (_homeAccountId is not null)
{
IAccount? saved = accounts.FirstOrDefault(
a => a.HomeAccountId?.Identifier == _homeAccountId);
if (saved is not null) { return saved; }
}
// 候補が1人だけなら、それを使ってよい
if (accounts.Count == 1) { return accounts[0]; }
// 0人、または複数居て決め手がない。
// ここで FirstOrDefault を使わないこと(下記)
return null;
}
}
لا تستقبل نتيجة GetAccountsAsync() بـ FirstOrDefault(). الحسابات في الذاكرة المؤقّتة ليست بالضرورة واحداً. بعد التبديل إلى حساب آخر، أو بعد استخدام عدّة أشخاص لجهاز مشترك، أو بعد الاختبار عبر مستأجرين — في أيّ منها تبقى عدّة حسابات. لا معنى لترتيب التعداد، لذا فإنّ FirstOrDefault() يختار «من صادف أن يكون في المقدّمة» بصمت.
ما يجعل هذا خطيراً أنّ الاختيار الخاطئ لا يُظهر أيّ شاشة. إن كان رمز ذلك الحساب حيّاً في الذاكرة المؤقّتة ينجح AcquireTokenSilent ولا يفتح منتقي الحسابات. يعمل المستخدم وهو يظنّ أنّه هو، بينما يعرض ويحدّث بيانات Graph لشخص آخر. ولا يلاحظ أحد.
لذلك يقرّر المثال أعلاه بهذا الترتيب.
| الوضع | ماذا تفعل |
|---|---|
| الشخص الذي اختير في المرّة السابقة ما زال في الذاكرة المؤقّتة | جرّب silent بذلك الشخص |
| المرشّح شخص واحد فقط | جرّب silent بذلك الشخص |
| صفر أشخاص، أو عدّة أشخاص بلا مرجّح | لا تجرّب silent، ودع المستخدم يختار في الحوار |
HomeAccountId.Identifier سلسلة تمثّل «أيّ مستخدم في أيّ مستأجر»، فإن حفظتها دخلت في المرّة التالية بنفس الشخص (ليست رمزاً، فلا حاجة إلى معاملتها كمعلومة حسّاسة). إن أردت جعل حساب Windows المسجَّل دخوله افتراضيّاً، يمكن تصميم السطر الأخير ليكون PublicClientApplication.OperatingSystemAccount. ما يجب تجنّبه هو «القرار بالترتيب» فقط.
جانب الاستدعاء يكون كالتالي. نفِّذ CreateAsync مرّة واحدة عند تشغيل التطبيق واحتفظ بالقيمة المعادة.
// フォームのフィールドとして持つ(アプリで1インスタンス)
private AuthService _authService;
// ボタンのClickハンドラーなどから。WPFは4章と同じくWindowInteropHelperでHWNDを取る
private async Task SignInAsync()
{
_authService ??= await AuthService.CreateAsync(this.Handle);
var result = await _authService.AcquireTokenAsync();
this.Text = $"مسجَّل الدخول: {result.Account.Username}";
}
بهذا الواحد تكتمل ثلاثة مسارات: على الأجهزة التي يعمل فيها WAM يكفي اختيار حساب Windows، وعلى التي لا يعمل فيها يفتح متصفّح النظام، وبعد إعادة التشغيل يحصل صامت من الذاكرة المؤقّتة. يبقى أن تتأكّد أنّ جانب تسجيل التطبيق يحوي عناوين جدول القسم 3.2 (على الأقلّ ms-appx-web://... وhttp://localhost).
6. استمرار ذاكرة تخزين الرموز ── لا تُظهر شاشة تسجيل الدخول عند كلّ إعادة تشغيل
ذاكرة تخزين الرموز في MSAL.NET في الذاكرة فقط افتراضيّاً، وفي تطبيقات سطح المكتب يقع تنفيذ الاستمراريّة على عاتق التطبيق. إن لم تُستمرّ، يفشل AcquireTokenSilent عند كلّ إعادة تشغيل للعملية فيسقط إلى تسجيل دخول تفاعليّ.7 سبب استشارة «كان الاختبار جيّداً ثمّ اشتكى الميدان من ظهور شاشة تسجيل الدخول كلّ صباح» هو هذا في الغالب.
الموصى به رسميّاً استخدام مكتبة الذاكرة المؤقّتة عبر المنصّات Microsoft.Identity.Client.Extensions.Msal (NuGet).3
using Microsoft.Identity.Client.Extensions.Msal;
var storageProperties = new StorageCreationPropertiesBuilder(
"msal_cache.dat",
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"KomuraSoft", "InventoryApp"))
.Build();
var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties);
cacheHelper.RegisterCache(_app.UserTokenCache); // Build() 直後に1回登録
على Windows تُحفَظ الذاكرة المؤقّتة مشفَّرة. مثال التنفيذ الذاتيّ في الوثائق الرسميّة يشفّر الرمز بـ ProtectedData (DPAPI، DataProtectionScope.CurrentUser) ويحفظه في ملفّ، وExtensions.Msal مكتبة تبلغ بذلك جودة المنتج.3 مبدأ «أسرار المستخدم تُحمى بـ DPAPI في نطاق المستخدم» هو نفسه حديث ملفّات الإعداد في «حفظ المعلومات السرّيّة في تطبيقات Windows - تجنّب الإعدادات بنصّ صريح عبر DPAPI». عامل التنفيذ الذاتيّ الذي يحفظ ذاكرة تخزين الرموز JSON بنصّ صريح بنفس خطورة حفظ سلسلة الاتّصال بنصّ صريح.
ثلاث ملاحظات تشغيل.
- حتّى عند استخدام WAM يلزم استمرار ذاكرة التخزين. لأنّ MSAL يواصل حفظ رمز الهوية وبيانات وصف الحساب في ذاكرته الخاصّة.9
- مكان الحفظ الأساسيّ
%LOCALAPPDATA%\اسم الشركة\اسم التطبيق. بسبب ربط DPAPI لا يمكن فكّ التشفير على جهاز آخر أو لمستخدم آخر، لكن الضرر يقتصر على فشل الحصول الصامت والعودة إلى إعادة تسجيل الدخول. - «تسجيل الخروج» يُنفَّذ بحذف الحسابات التي يعدّدها
GetAccountsAsyncعبرRemoveAsync. لا حاجة إلى حذف ملفّ الذاكرة المؤقّتة. غير أنّRemoveAsyncيحذف ذاكرة MSAL المحلّيّة فقط، وتبقى جلسات WAM والمتصفّح وتسجيل دخول Windows كما هي. في الحوار التالي قد يُعاد تسجيل الدخول الصامت لنفس الحساب، لذا إن أردت تبديل الحساب على جهاز مشترك فأضفWithPrompt(Prompt.SelectAccount)إلىAcquireTokenInteractiveلإظهار شاشة الاختيار حتماً، أو اجمع بحسب المتطلّب نقطة نهاية تسجيل الخروج في المستأجر، وميّز في التصميم بين «حذف الذاكرة المحلّيّة» و«تسجيل الخروج الحقيقيّ».
7. ماذا تفعل بالرمز الذي حصلت عليه ── ثلاثة تشكيلات
بعد نجاح المصادقة تنقسم الاستخدامات إلى ثلاثة أنماط. وما يلزم من إعداد يتغيّر بحسب المدى.
| التشكيل | الرمز المستخدم | ما يلزم إضافيّاً |
|---|---|---|
| (1) تسجيل الدخول فقط | رمز الهوية (AuthenticationResult.Account / ClaimsPrincipal) |
لا شيء (User.Read فقط) |
| (2) استدعاء Microsoft Graph | رمز وصول موجَّه إلى Graph | أذونات Graph الموافقة بحسب واجهة API المطلوبة |
| (3) حماية واجهة API خاصّة بالشركة | رمز وصول موجَّه إلى واجهة API الشركة | تسجيل تطبيق لجانب API ونشر النطاق، والتحقّق من الرمز في جانب API |
7.1 تشكيل تسجيل الدخول فقط ── البداية الأصغر
إن أردت «استبدال مطابقة كلمة المرور الذاتيّة فقط دون استدعاء واجهات سحابيّة»، يكفي مطابقة معلومات الحساب في نتيجة تسجيل الدخول بجدول الصلاحيّات داخل التطبيق. استبدل مفتاح جدول users بـ معرّف الكائن في Entra (لا يتغيّر حتّى إن تغيّر UPN عند تغيير اللقب)، واحذف عمود كلمة المرور. يمكن استبدال المصادقة وحدها دون تغيير تصميم قاعدة البيانات المحلّيّة، لذا فهو التشكيل الأسهل توصيةً كخطوة أولى.
7.2 استدعاء Microsoft Graph
إن رميت رمز وصول User.Read كما هو إلى Microsoft Graph، حصلت على ملفّ المستخدم المسجَّل دخوله وصورته.
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", result.AccessToken);
var me = await http.GetStringAsync("https://graph.microsoft.com/v1.0/me");
إن وسّعت إلى التقويم أو إرسال البريد أو إشعارات Teams، فأضف الإذن المقابل (Mail.Send مثلاً) وأعد أخذ موافقة المسؤول. تصميم توجيه بريد الإشعارات من التطبيقات الداخليّة إلى Graph يتوافق مع المسار الذي عالجناه في «كيف تصمّم الشركات الصغيرة والمتوسّطة البريد الجماعيّ دون أن تحبس نفسها مع مزوّد واحد».
7.3 حماية واجهة API خاصّة بالشركة ── حتّى التحقّق من audience والنطاق
إن استدعى تطبيق سطح المكتب واجهة API ويب خاصّة بالشركة، فأنشئ تسجيلاً منفصلاً لجانب API، وانشر نطاقاً مثل api://{معرّف عميل API}/access_as_user، واطلب الرمز بذلك النطاق من جانب سطح المكتب. المهمّ في جانب API أنّ الوثائق تنصّ صراحة على أنّ [Authorize] وحده لا يكفي.11 ما يجب التحقّق منه ثلاث طبقات.
- التوقيع والمُصدِر: هل JWT صادر عن Entra ID للمستأجر الصحيح (في ASP.NET Core + Microsoft.Identity.Web تعالجه البرمجيّة الوسيطة)
- audience (
aud): هل وجهة الرمز واجهة API هذه نفسها. لا تُعِد استخدام رمز Graph على واجهة API الشركة - النطاق (ادّعاء
scp): هل النطاق المتوقَّع موجود. في Microsoft.Identity.Web يمكن التصريح بسمة[RequiredScope("access_as_user")]11
إن أسقطت 2 و3 حصلت على «واجهة API يمرّ فيها أيّ شيء يشبه رمز Entra». اجعله موضوع مراجعة تصميم مع بنود الاتّصال والتحقّق من الإدخال في «قائمة تحقّق للحدّ الأدنى من الأمان في تطوير تطبيقات Windows».
8. قرار التبنّي ── هل يجب إدخال مصادقة Entra في أداة داخليّة بحتة
ليس الجواب أن تُدخلها في كلّ تطبيق داخليّ. نرسم أوّلاً ترتيب الحكم. يأتي افتراضان أوّلاً: الشبكة وأساس الهويّة، ثمّ ننظر إلى ظروف التطبيق.
flowchart TD
Q1{"هل تصل بيئة تشغيل التطبيق<br/>إلى Entra ID؟"}
Q1 -->|"توجد منطقة بلا اتّصال بالكامل"| NG1["غير ممكن، أو يلزم تصميم<br/>تحديث الرمز يحتاج شبكة"]
Q1 -->|"يمكن الوصول"| Q2{"ما أساس هويّة المؤسّسة؟"}
Q2 -->|"Active Directory محلّي فقط"| ALT1["انظر في مصادقة Windows المتكاملة"]
Q2 -->|"Google Workspace فقط"| ALT2["انظر في آليّة جانب Google"]
Q2 -->|"Microsoft 365 / Entra ID"| Q3{"هل للتطبيق مفهوم تسجيل دخول،<br/>أو استدعاء API، أو متطلّب مراجعة؟"}
Q3 -->|"لا (أداة تحويل أحاديّة الوظيفة مثلاً)"| NO["لا تُدخل<br/>تسجيل دخول Windows يكفي"]
Q3 -->|"نعم"| YES["أدخل<br/>يمكنك التخلّي عن إدارة كلمات المرور الذاتيّة كلّها"]
الشكل 3: الحكم من الأعلى. ضيّق أوّلاً بمتطلّب العمل بلا اتّصال وبأساس الهويّة، ثمّ انظر حاجة جانب التطبيق.
| الوضع | التوصية | السبب |
|---|---|---|
| Microsoft 365 / Entra ID معتمَد على مستوى الشركة + للتطبيق مفهوم تسجيل دخول | أدخل | يختفي دين إدارة كلمات المرور الذاتيّة كلّه. تكلفة التنفيذ صغيرة |
| التطبيق يستدعي واجهة API ويب للشركة أو موارد سحابيّة | أدخل | حماية API تحتاج أساس مصادقة. أوثق من اختراع رمز ذاتيّ |
| يوجد متطلّب مراجعة (تسجيل من استخدم ومتى، إلزام MFA) | أدخل | تتوحّد سجلّات تسجيل الدخول والوصول المشروط في جانب المستأجر |
| أداة أحاديّة الوظيفة بلا مفهوم تسجيل دخول (محوّل، عارض، إلخ) | غير لازمة | لا دافع لإضافة المصادقة. تسجيل دخول Windows يكفي |
| تعمل في بيئة بلا اتّصال بالكامل (خط إنتاج مغلق، حاسوب محمول) | غير ممكن أو يلزم تصميم | أوّل تسجيل دخول وتحديث الرمز يحتاجان شبكة |
| Entra ID غير معتمَد (Active Directory محلّي فقط، أو Google Workspace فقط) | انظر في حلّ آخر | الأوّل مصادقة AD (مصادقة Windows المتكاملة)، والثاني آليّة جانب Google |
انتبه بوجه خاصّ لمتطلّب العمل بلا اتّصال. يستطيع AcquireTokenSilent أن يعيد الرمز بلا شبكة طالما رمز الوصول في الذاكرة المؤقّتة صالح (تجريبيّاً نحو ساعة وزيادة قليلاً)، لكن إن انتهت الصلاحيّة لزم التحديث شبكة. قبل التبنّي قابل هذا الافتراض عن العمر بنمط الاستخدام في الميدان.
9. مطبّات التشغيل ── الاستفسارات التي ترد بعد التبنّي
التبنّي ليس النهاية. في مرحلة التشغيل استفسارات معتادة. نكتبها استباقاً.
- «كان يعمل حتّى الأمس ثمّ تعذّر تسجيل الدخول فجأة»: المتّهم الأوّل تغيير سياسة الوصول المشروط. إذا فعّل قسم نظم المعلومات «حظر الأجهزة غير المسجَّلة» مثلاً، يبدأ تسجيل الدخول بالفشل دون أن يغيّر التطبيق شيئاً. أسرع فصل هو النظر في سبب الخطأ للمستخدم المعنيّ في سجلّات تسجيل الدخول بمركز إدارة Entra. تشكيل WAM يرفع القدرة على الاستجابة لمتطلّبات السياسة، فيقلّ هذا الاحتكاك نفسه.1
- «وصل إشعار بانتهاء أجل السرّ، أهذا التطبيق بخير؟»: العميل العامّ لا يملك سرّاً ولا شهادة أصلاً، فلا انتهاء أجل. إن ورد هذا الاستفسار فإمّا خلط مع تسجيل عميل سرّيّ، أو أنّ أحداً أنشأ سرّاً غير لازم في تسجيل العميل العامّ (في الحالة الثانية احذفه). أنّ توقّف التشغيل بسبب انتهاء أجل السرّ لا يحدث بنيويّاً مزيّة خفيّة لهذا التشكيل.
- «عند أوّل تشغيل تظهر «يلزم موافقة المسؤول»»: سهو موافقة المسؤول في القسم 3.3. إن أضفت أذونات لاحقاً يظهر العرض نفسه حتّى تعيد أخذ موافقة الإضافة.
- «لم يعمل بعد إدراجه في دفعة ليليّة»: كما في القسم 5.2، WAM يفترض جلسة تفاعليّة. المعالجة غير المراقبة تصميم منفصل بصلاحيّات التطبيق، لا بإعادة استخدام رمز تفويض المستخدم.
- التوزيع والتحديث: محيط MSAL نشط في التصحيح، ويلزم آليّة لتوزيع تحديث المكتبة على كلّ الأجهزة. فكّر فيه مع التحقّق من مسار التحديث الذي كتبناه في «تصميم أمان التحديث التلقائيّ».
10. الخلاصة
دعم مصادقة Entra ID في تطبيقات WinForms / WPF يتجمّع في ستّ نقاط.
- التوقّف عن إدارة كلمات المرور الذاتيّة هو الهدف نفسه. الحفظ وإعادة التعيين ومعالجة المغادرين والمراجعة تتوحّد في جانب المستأجر
- تطبيق سطح المكتب عميل عامّ. لا يمكنه حمل سرّ، ولا يحتاجه
- ROPC في طريق الإلغاء. لا تصنع شاشة تستقبل اسم المستخدم وكلمة المرور في تطوير جديد
- التنفيذ نمط MSAL.NET
AcquireTokenSilent→AcquireTokenInteractiveوحده - على Windows وسيط WAM (
WithBroker) لـ SSO والوصول المشروط وWindows Hello - استمرار ذاكرة تخزين الرموز (Extensions.Msal / حماية DPAPI) يُدرَج من البداية
التشكيل الأصغر «استبدال تسجيل الدخول فقط» (القسم 7.1) يحصر أثر التطبيق القائم في محيط شاشة تسجيل الدخول وجدول المستخدمين، وغالباً ما ينتهي بتعديل بمقياس أيّام. أمّا إذا تداخل الوصول المشروط أو متطلّب العمل بلا اتّصال، فيلزم حكم تصميم يراعي إعداد جانب المستأجر وواقع العمل. إن تردّدت في أيّ تشكيل تبلغه بتطبيقك، أو في كيف تقطع خطوات الانتقال من المصادقة الذاتيّة، يمكننا المساعدة.
مقالات ذات صلة
- ما هو GCPW - طريقة التعامل مع تسجيل دخول Windows بمصادقة Google
- أفضل الممارسات في DPAPI لإبعاد الأسرار عن إعدادات النصّ الصريح في تطبيقات Windows
- قائمة تحقّق للحدّ الأدنى من الأمان في تطوير تطبيقات Windows
- أساسيّات أمان ميزة التحديث التلقائيّ - الأنماط السيّئة وأفضل الممارسات
مجالات الاستشارة ذات الصلة
تتعامل شركة كومورا سوفت ذ.م.م. مع دمج مصادقة Entra ID في تطبيقات WinForms / WPF القائمة (تصميم تسجيل التطبيق، وتنفيذ MSAL.NET، وخطّة الانتقال من المصادقة الذاتيّة)، ومراجعة تصميم التحقّق من الرموز لواجهات API الخاصّة بالشركة، وفصل أعطال تسجيل الدخول المرتبطة بالوصول المشروط.
- تطوير تطبيقات Windows
- الاستشارة التقنيّة ومراجعة التصميم
- الاستفادة من الأصول القائمة ودعم الترحيل
- تواصل معنا
روابط مرجعية
-
Microsoft Learn, Using MSAL.NET with Web Account Manager (WAM). حول مزايا الوسيط (تعزيز الأمان، ودعم Windows Hello والوصول المشروط وFIDO، ومنتقي الحسابات، وحماية الرموز)، وMSAL.NET 4.52.0+ وحزمة Microsoft.Identity.Client.Broker، وإلزام WithBroker ومقبض النافذة الأم، وعنوان إعادة التوجيه ms-appx-web، وأنظمة التشغيل المدعومة والرجوع الاحتياطيّ، وقيود الجلسة التفاعليّة. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11
-
Microsoft Learn, Using web browsers (MSAL.NET). حول جدول دعم المتصفّح حسب الإطار (افتراض .NET Framework 4.6.2+ مضمَّن، و.NET 6+ متصفّح النظام فقط)، وحاجة متصفّح النظام إلى عنوان إعادة توجيه http://localhost، والتبديل عبر WithUseEmbeddedWebView. ↩ ↩2
-
Microsoft Learn, Token cache serialization. حول التوصية بأن تستخدم تطبيقات سطح المكتب الذاكرة المؤقّتة عبر المنصّات في Microsoft.Identity.Client.Extensions.Msal، واستخدام MsalCacheHelper، ومثال التسلسل الذاتيّ عبر ProtectedData (DPAPI، نطاق CurrentUser). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Desktop app that calls web APIs: Code configuration. حول عنوان URI لإعادة التوجيه لتطبيقات سطح المكتب (منصّة الجوّال وسطح المكتب، nativeclient / localhost)، ومعنى إعداد «السماح بتدفّقات العميل العامّ». ↩ ↩2 ↩3
-
Microsoft Learn, Microsoft identity platform and OAuth 2.0 Resource Owner Password Credentials. حول أنّه لا ينبغي استخدام ROPC، وعدم توافقه مع المصادقة متعدّدة العوامل وحظره، واتّجاه استبعاد التطبيقات المعتمِدة على ROPC، ووجوب انتقال تطبيقات سطح المكتب إلى مصادقة قائمة على الوسيط. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Desktop app that calls web APIs: Acquire a token using username and password. حول اعتبار تدفّق اسم المستخدم وكلمة المرور (ROPC) مُلغى (deprecated) بسبب المخاطر الأمنيّة، والإرشاد إلى دليل الانتقال، وقيود عدم دعم المصادقة متعدّدة العوامل والوصول المشروط وSSO. ↩ ↩2
-
Microsoft Learn, Get a token from the token cache using MSAL.NET. حول النمط الموصى به لاستدعاء AcquireTokenSilent أوّلاً والرجوع احتياطيّاً إلى التفاعليّ عند MsalUiRequiredException، والتحديث التلقائيّ عبر ذاكرة التخزين ورمز التحديث، ومسح ذاكرة التخزين بحذف الحساب. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Register an application with the Microsoft identity platform. حول خطوات تسجيل التطبيق في مركز إدارة Entra، واختيار أنواع الحسابات المدعومة، والحصول على معرّف العميل، وموافقة المسؤول. ↩ ↩2
-
Microsoft Learn, Desktop app that calls web APIs: Acquire a token by using WAM. حول لزوم استمرار ذاكرة تخزين الرموز حتّى عند استخدام WAM، والنمط الموصى به لتسجيل الدخول الصامت عبر OperatingSystemAccount، وإعداد عنوان إعادة التوجيه في جانب تسجيل التطبيق. ↩ ↩2 ↩3
-
Microsoft Learn, Default reply URI. حول اعتماد عنوان إعادة التوجيه الذي يضبطه WithDefaultRedirectUri على المنصّة (.NET Framework لسطح المكتب https://login.microsoftonline.com/common/oauth2/nativeclient، و.NET Core http://localhost). ↩ ↩2 ↩3
-
Microsoft Learn, Protected web API: Verify scopes and app roles. حول أنّ سمة [Authorize] وحدها لا تكفي، ولزوم التحقّق من ادّعاء scp (النطاق)، والتحقّق التصريحيّ عبر سمة RequiredScope في Microsoft.Identity.Web. ↩ ↩2
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
إقامة تطبيقات Windows في علبة النظام والإشعارات المنبثقة ── مطبات NotifyIcon وكيفية اختيار AppNotification
نرتّب إقامة تطبيقات Windows للأعمال في علبة النظام والإشعارات المنبثقة. نشرح الاستخدام الصحيح لـ NotifyIcon، وإعادة التسجيل عند إعادة تشغ...
الاختبار الآلي لواجهة المستخدم في تطبيقات سطح مكتب Windows ── آلية UI Automation وبناء اختبارات لا تنكسر بسهولة باستخدام FlaUI
نرتّب الاختبار الآلي لواجهة المستخدم في تطبيقات WinForms/WPF انطلاقاً من آلية Windows UI Automation. نشرح التنفيذ الأدنى عبر FlaUI، وتصمي...
دعم DPI العالي في WPF ── أسباب الضبابية والتلطّخ رغم أنه «يفترض أن يكون محصّناً ضد DPI» وطرق العلاج
WPF واعٍ بـ System DPI، لكن نقل النافذة إلى شاشة مختلفة DPI يجعل الشاشة كلّها تتلطّخ، وتصبح الصور النقطية ضبابية. نرتّب تشخيص السبب، ودعم...
دعم DPI العالي في WinForms ── أسباب الضبابية والانهيار على شاشات 4K والمعالجة الواقعية
نرتّب أسباب ضبابية تطبيقات WinForms وانهيار تخطيطها على شاشات 4K انطلاقاً من DPI virtualization وأوضاع إدراك DPI (System Aware / Per-Moni...
انتهاء خدمة برامج تشغيل الطابعات في Windows ── كيف تستعد تطبيقات الأعمال لطباعة التقارير والملصقات
تُنهى Microsoft تدريجيّاً خدمة برامج تشغيل الطابعات v3/v4، ومن تمّوز/يوليو 2026 يُفضَّل برنامج تشغيل فئة IPP. نرتّب في جداول قرار ما يختف...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
ندعم تطوير برامج ويندوز للأعمال، وتكامل الأجهزة، وأدوات التواصل.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- ما فائدة إدخال مصادقة Entra ID في تطبيقات WinForms/WPF؟
- يصبح حفظ كلمات المرور، ومعالجة إعادة تعيينها، وإيقاف حسابات من يغادرون العمل، ومراجعة سجلّات تسجيل الدخول، كلّها من مهام المستأجر، فيتقلّص نطاق مسؤوليّة التطبيق تقلّصاً كبيراً. فمن يغادر العمل يكفي تعطيل حسابه في Entra ID ليصبح عاجزاً فوراً عن تسجيل الدخول في جميع التطبيقات، كما تسري المصادقة متعدّدة العوامل والوصول المشروط مباشرةً على التطبيقات الداخليّة. والتنفيذ أيضاً يقتصر على مكتبة MSAL.NET وبضع عشرات من أسطر الشيفرة. لا يكاد يوجد سبب يدعو مؤسّسة اعتمدت Microsoft 365 بالفعل إلى الاستمرار في مصادقة ذاتيّة الصنع.
- هل يمكن استخدام طريقة استقبال اسم المستخدم وكلمة المرور عبر شاشة خاصّة للمصادقة (ROPC)؟
- اعتبر أنّ اعتماده في تطوير جديد ممنوع. فـ ROPC الموجَّه للعملاء العامّين مذكور رسميّاً على أنّه «أُلغِيَ (deprecated) بسبب المخاطر الأمنيّة»، وقد نُشر دليل للانتقال منه. ولأنّه غير متوافق مع المصادقة متعدّدة العوامل والوصول المشروط، فإنّ المستخدمين الذين تُلزمهم سياسة المستأجر بالمصادقة متعدّدة العوامل يُحظَرون ولا يستطيعون تسجيل الدخول. وبما أنّ إلزام المصادقة متعدّدة العوامل قد يحدث في أيّ وقت عبر إعداد جانب المستأجر، فحتّى لو كان يعمل الآن، فثمّة خطر أن يعجز جميع المستخدمين فجأة يوماً ما عن تسجيل الدخول.
- هل من الأفضل استخدام وسيط WAM؟
- على Windows هو موصى به. فبسطر واحد من WithBroker تحصل على SSO مع الحساب المسجَّل دخوله بالفعل في Windows، ودعم للوصول المشروط وWindows Hello ومفاتيح FIDO، وربط رمز التحديث بالجهاز. فإن كان الحاسوب الداخليّ منضمّاً إلى Entra، تكتمل عمليّة تسجيل الدخول بمجرّد اختيار حساب Windows بعد تشغيل التطبيق، دون إدخال كلمة مرور. لكنّه مخصّص لـ Entra ID فقط، ولديه قيد يجعله يُصدر خطأً بحسب التصميم خارج جلسات المستخدم التفاعليّة، كخدمات Windows أو جدولة المهام.
- لماذا تظهر شاشة تسجيل الدخول في كلّ مرّة يُعاد فيها تشغيل التطبيق؟
- لأنّ ذاكرة تخزين الرموز غير مستمرّة. فذاكرة تخزين الرموز في MSAL.NET تبقى افتراضيّاً في الذاكرة فقط، وفي تطبيقات سطح المكتب يقع تنفيذ الاستمراريّة على عاتق التطبيق نفسه. والموصى به رسميّاً هو حزمة Microsoft.Identity.Client.Extensions.Msal، حيث تُحفَظ الذاكرة المؤقّتة مشفَّرة على Windows. وحتّى عند استخدام WAM، تبقى استمراريّة ذاكرة التخزين لازمة لحفظ رمز الهوية وبيانات وصف الحساب.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.