تسجيل الدخول بدون كلمة مرور باستخدام .NET Identity: دليل عملي لتطبيق المصادقة السلسة
لماذا أصبح تسجيل الدخول بدون كلمة مرور خياراً مهماً؟
مع ازدياد عدد التطبيقات والخدمات الرقمية، بات المستخدم يواجه عبئاً متكرراً يتمثل في تذكر عشرات كلمات المرور وإدارتها باستمرار. هذه التجربة لا تؤثر فقط في راحة المستخدم، بل قد تتسبب أيضاً في انخفاض معدلات التسجيل والدخول داخل التطبيق. لذلك أصبح نموذج Passwordless Login من الأساليب الحديثة التي تحسن تجربة الاستخدام وتقلل الاحتكاك أثناء المصادقة.
في هذا الدليل سنبني آلية تسجيل دخول بدون كلمة مرور لتطبيقات API باستخدام .NET Identity، بحيث يتلقى المستخدم رمز تحقق عبر البريد الإلكتروني أو الهاتف، ثم يستخدمه لإثبات هويته دون الحاجة إلى إدخال كلمة مرور تقليدية.

كيف تعمل آلية الدخول بدون كلمة مرور؟
الفكرة الأساسية بسيطة: بدلاً من مطالبة المستخدم بكلمة مرور، يقوم التطبيق بإنشاء رمز مؤقت Token ثم يرسله إلى وسيلة يمتلكها المستخدم وحده، مثل البريد الإلكتروني أو رقم الهاتف. بعد ذلك يرسل المستخدم هذا الرمز إلى نقطة تحقق داخل النظام لإثبات ملكيته للحساب.
هذا التدفق مناسب جداً للتطبيقات الحديثة، خاصة عندما يكون الهدف هو تسهيل الوصول وتقليل الحواجز أمام المستخدمين الجدد.

ما هو .NET Identity ولماذا نستخدمه؟
حزمة .NET Identity توفر بنية قوية لإدارة المستخدمين والرموز والأدوار والبيانات الشخصية وعمليات المصادقة داخل تطبيقات ASP.NET Core. كما أنها تقدم مزودات جاهزة لتوليد الرموز الخاصة بتأكيد البريد الإلكتروني أو تغيير الهاتف أو تنفيذ إجراءات تحقق حساسة.
في هذا السيناريو سنستفيد من آليات توليد الرموز داخل Identity لبناء نظام دخول بدون كلمة مرور بطريقة منظمة وقابلة للتوسع.
أنواع مزودي الرموز في Identity
يوفر .NET Identity نوعين رئيسيين من مزودي الرموز يمكن البناء عليهما:
TotpSecurityStampBasedTokenProviderوهو مزود يعتمد على كلمات مرور مؤقتة مرتبطة بالوقتTime-based One Time Password.DataProtectorTokenProviderوهو مزود يستخدم آليات حماية وتشفير لتوليد رموز أكثر مرونة من حيث مدة الصلاحية.
المزود TotpSecurityStampBasedTokenProvider
هذا المزود ينشئ رموزاً مؤقتة قصيرة العمر، وغالباً ما تكون صالحة لبضع دقائق فقط. يعتمد في توليد الرمز على بيانات مثل البريد الإلكتروني أو رقم الهاتف أو معرف المستخدم، بالإضافة إلى قيمة Security Stamp الخاصة بالحساب.
كما يوفر Identity أصنافاً جاهزة مبنية عليه مثل EmailTokenProvider وPhoneNumberTokenProvider.
المزود DataProtectorTokenProvider
إذا كنت تريد إنشاء رمز يمتد لفترة أطول نسبياً ويعتمد على حماية تشفيرية أكثر مرونة، فإن DataProtectorTokenProvider يعد خياراً مناسباً. هذا المزود يعتمد على DataProtector وخوارزميات تشفير داخلية لحماية الرموز والتحقق منها.
في هذا الشرح سنبني مزوداً مخصصاً مشتقاً من DataProtectorTokenProvider بحيث تكون صلاحية الرمز 10 دقائق.
إعداد مشروع .NET Web API
لنبدأ بإنشاء مشروع جديد:
dotnet new webapi --name NoPasswordProject
بعد ذلك أضف الحزم المطلوبة:
dotnet add package Microsoft.EntityFrameworkCore.InMemory --version 5.0.4
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 5.0.4
في هذا الدليل سنستخدم قاعدة بيانات داخل الذاكرة InMemory Database لتبسيط التجربة، لكن في بيئات الإنتاج يمكنك استخدام SQL Server أو أي محرك قواعد بيانات مناسب.
ملاحظة مهمة: قاعدة البيانات من نوع InMemory تُفقد بيانات المستخدمين كلما أُعيد تشغيل الخادم.
إنشاء مزود رموز مخصص
نحتاج الآن إلى إنشاء مزود رموز خاص بنا يمنح المستخدم رمزاً صالحاً لمدة عشر دقائق.
ملف NPTokenProvider.cs
أنشئ ملفاً جديداً باسم NPTokenProvider.cs. يشير الاختصار NP إلى No Password.
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
public class NPTokenProvider<TUser> : DataProtectorTokenProvider<TUser>
where TUser : IdentityUser
{
public NPTokenProvider(
IDataProtectionProvider dataProtectionProvider,
IOptions<NPTokenProviderOptions> options,
ILogger<NPTokenProvider<TUser>> logger)
: base(dataProtectionProvider, options, logger)
{
}
}
في هذا الصنف نقوم بوراثة DataProtectorTokenProvider دون إضافة منطق خاص معقد، لكننا نمرر إليه إعدادات مخصصة من خلال NPTokenProviderOptions.
ملف NPTokenProviderOptions.cs
الآن أنشئ ملفاً جديداً باسم NPTokenProviderOptions.cs وضع فيه الكود التالي:
using System;
using Microsoft.AspNetCore.Identity;
public class NPTokenProviderOptions : DataProtectionTokenProviderOptions
{
public NPTokenProviderOptions()
{
Name = "NPTokenProvider";
TokenLifespan = TimeSpan.FromMinutes(10);
}
}
هنا قمنا بتحديد اسم المزود في الخاصية Name وضبط مدة صلاحية الرمز عبر TokenLifespan. يمكنك تغيير القيمتين بما يناسب متطلبات مشروعك.
إنشاء DbContext للمشروع
غالبية التطبيقات تحتاج إلى طبقة لإدارة الاتصال بقاعدة البيانات. في منظومة Entity Framework Core يؤدي DbContext هذا الدور، إذ يتولى التعامل مع الجلسات والاستعلامات وحفظ الكيانات.
أنشئ ملفاً باسم NPDataContext.cs واجعله يرث من IdentityDbContext:
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class NPDataContext : IdentityDbContext
{
public NPDataContext(DbContextOptions<NPDataContext> options)
: base(options)
{
}
}
ضبط الإعدادات داخل Startup.cs
بعد إنشاء الأصناف الأساسية، حان وقت تسجيلها داخل الحاوية الخاصة بالاعتمادات Dependency Injection.
أضف الكود التالي داخل الدالة ConfigureServices:
var builder = services
.AddIdentityCore<IdentityUser>()
.AddEntityFrameworkStores<NPDataContext>();
var UserType = builder.UserType;
var provider = typeof(NPTokenProvider<>).MakeGenericType(UserType);
builder.AddTokenProvider("NPTokenProvider", provider);
services.AddDbContext<NPDataContext>(
options => options.UseInMemoryDatabase(Guid.NewGuid().ToString()));
services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ExternalScheme;
});
ولا تنسَ إضافة السطر التالي داخل الدالة Configure قبل app.UseAuthorization();:
app.UseAuthentication();
هذه الخطوة ضرورية لتفعيل مسار المصادقة داخل التطبيق.
إنشاء المتحكم NoPasswordController
سننشئ الآن متحكماً يحتوي على واجهتي API أساسيتين: واحدة لإرسال رمز الدخول، والثانية للتحقق منه.
أنشئ ملفاً باسم NoPasswordController.cs داخل مجلد Controllers:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace NoPasswordProject.Controllers
{
[ApiController]
[Route("[controller]/[action]")]
public class NoPasswordController : ControllerBase
{
private readonly UserManager<IdentityUser> _userManager;
public NoPasswordController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
}
}
يعتمد هذا المتحكم على UserManager الذي يوفر عمليات إنشاء المستخدمين والبحث عنهم وتوليد الرموز والتحقق منها.
بناء واجهة Login API
وظيفة هذه الواجهة استقبال البريد الإلكتروني، ثم إنشاء المستخدم إن لم يكن موجوداً، وبعدها توليد رمز تسجيل دخول خاص به.
أضف الدالة التالية داخل المتحكم:
[HttpGet]
public async Task<ActionResult<string>> Login([FromQuery] string Email)
{
// Create or Fetch your user from the database
var User = await _userManager.FindByNameAsync(Email);
if (User == null)
{
User = new IdentityUser();
User.Email = Email;
User.UserName = Email;
var IdentityResult = await _userManager.CreateAsync(User);
if (IdentityResult.Succeeded == false)
{
return BadRequest();
}
}
var Token = await _userManager.GenerateUserTokenAsync(
User,
"NPTokenProvider",
"nopassword-for-the-win");
// DON'T RETURN THE TOKEN.
// SEND IT TO THE USER VIA EMAIL.
return NoContent();
}
ما الذي يحدث هنا؟
- يبحث النظام عن المستخدم بناءً على البريد الإلكتروني باستخدام
FindByNameAsync(). - إذا لم يكن المستخدم موجوداً، يتم إنشاؤه مع ضرورة تعيين الخاصيتين
UserNameوEmail. - يتم توليد رمز باستخدام
GenerateUserTokenAsync(). - يجب عدم إعادة الرمز مباشرة في الاستجابة، بل إرساله إلى المستخدم عبر البريد الإلكتروني أو رسالة نصية.
من الأفضل أن يتضمن البريد الإلكتروني رابطاً يقود إلى الواجهة الأمامية Front-end، وعند نقر المستخدم عليه، ترسل الواجهة الطلب إلى نقطة التحقق.
أفضل ممارسة أمنية مهمة
رغم أن المثال التعليمي يُظهر الفكرة الأساسية، فإن بيئات الإنتاج تتطلب احتياطات إضافية، مثل:
- تحديد عدد محاولات الطلب والتحقق
Rate Limiting. - تسجيل الأحداث الأمنية الحساسة.
- استخدام قناة إرسال موثوقة للبريد أو الرسائل.
- إبطال الرموز القديمة عند الحاجة.
- إصدار
JWT Bearer Tokenبعد نجاح التحقق بدلاً من الاكتفاء برد نصي بسيط.
بناء واجهة Verify API
الآن نضيف واجهة التحقق التي تستقبل البريد الإلكتروني والرمز المرسل للمستخدم.
[HttpGet]
public async Task<ActionResult<string>> Verify([FromQuery] string Token, [FromQuery] string Email)
{
// Fetch your user from the database
var User = await _userManager.FindByNameAsync(Email);
if (User == null)
{
return NotFound();
}
var IsValid = await _userManager.VerifyUserTokenAsync(
User,
"NPTokenProvider",
"nopassword-for-the-win",
Token);
if (IsValid)
{
// TODO: Generate a bearer token
var BearerToken = "";
return BearerToken;
}
return Unauthorized();
}
تعتمد هذه الدالة على الخطوات التالية:
- البحث عن المستخدم بواسطة البريد الإلكتروني.
- إذا لم يُعثر عليه، يتم إرجاع الحالة
404 Not Found. - التحقق من الرمز عبر
VerifyUserTokenAsync(). - إذا كان الرمز صحيحاً، تُنشئ رمز وصول من نوع
Bearer Token. - إذا فشل التحقق، تُعاد الحالة
401 Unauthorized.
انتبه إلى أن قيمة purpose المستخدمة في VerifyUserTokenAsync() يجب أن تطابق تماماً القيمة المستخدمة أثناء إنشاء الرمز، وإلا سيفشل التحقق حتى لو كان الرمز صحيحاً.
مقارنة سريعة بين النهج التقليدي ونهج Passwordless
| المعيار | تسجيل الدخول بكلمة مرور | تسجيل الدخول بدون كلمة مرور |
|---|---|---|
| تجربة المستخدم | تحتاج إلى تذكر كلمة المرور | أبسط وأكثر سلاسة |
| معدل الاحتكاك | أعلى | أقل |
| إعادة تعيين الوصول | شائعة بسبب نسيان كلمة المرور | أقل اعتماداً على كلمات المرور |
| الأمان | يعتمد على قوة كلمة المرور | يعتمد على أمان البريد أو الهاتف والرموز المؤقتة |
| الملاءمة للتطبيقات الحديثة | مناسب | ممتاز للتجارب السريعة والمرنة |
نصائح عملية قبل اعتماد الحل في الإنتاج
- استخدم مزود بريد احترافي لإرسال الرسائل بسرعة وموثوقية.
- اجعل صلاحية الرمز قصيرة نسبياً لتقليل المخاطر الأمنية.
- اعتمد بروتوكول
HTTPSفي جميع البيئات. - لا تعرض الرمز في سجلات النظام
Logs. - فكر في ربط التحقق بعنوان جهاز أو جلسة محددة إذا كانت الحساسية الأمنية مرتفعة.
- أضف رسائل واضحة للمستخدم حول انتهاء صلاحية الرمز أو فشل التحقق.
لماذا يفيد هذا الأسلوب تحسين تجربة المستخدم ونتائج الأعمال؟
كلما أصبحت عملية الدخول أسهل، زادت احتمالية إكمال المستخدم للتسجيل أو العودة إلى التطبيق. تقليل الاعتماد على كلمات المرور يعني تقليل مشكلات النسيان، وخفض طلبات الدعم الفني المتعلقة بإعادة التعيين، ورفع معدلات الاحتفاظ بالمستخدمين. لهذا السبب تتجه كثير من المنتجات الحديثة إلى اعتماد حلول Passwordless كجزء من تحسين تجربة المستخدم وليس فقط كخيار تقني.
الخلاصة التقنية
يُعد تطبيق تسجيل الدخول بدون كلمة مرور باستخدام .NET Identity خياراً عملياً ومرناً للتطبيقات الحديثة، خاصة عند الرغبة في تقديم تجربة استخدام سريعة وآمنة نسبياً. من خلال تخصيص DataProtectorTokenProvider وتحديد مدة صلاحية مناسبة للرمز، يمكنك بناء تدفق مصادقة واضح وقابل للتطوير. وإذا دُمج هذا النهج مع إرسال آمن للرموز وإصدار Bearer Token بعد التحقق، فستحصل على حل متوازن يجمع بين سهولة الاستخدام والانضباط الأمني.