আজকের লেকচারে স্বাগতম!

কোথায় আছি আমরা? আপনি বর্তমানে Section 25: Identity-এর একটি চমৎকার পর্যায়ে আছেন। গত লেকচারগুলোতে আমরা User Registration এবং Password Complexity সেটআপ করা শিখেছি। আজ আমরা একটি অ্যাপ্লিকেশনের সবচেয়ে কমন এবং গুরুত্বপূর্ণ ফিচার শিখবো—Login এবং LogoutSignInManager ব্যবহার করে কীভাবে ইউজারের ইমেইল ও পাসওয়ার্ড ভেরিফাই করতে হয় এবং Authentication Cookie কীভাবে কাজ করে, তা আমরা বিস্তারিত জানবো। চলুন শুরু করি!


📝 Quick Summary for Revision

ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য পুরো লেকচারের মূল বিষয়গুলো নিচে লিস্ট করা হলো:

  • LoginDTO: লগইন ফর্মের ডেটা রিসিভ করার জন্য শুধুমাত্র Email এবং Password প্রপার্টি দিয়ে একটি DTO ক্লাস তৈরি করা।
  • Login View: Register ভিউয়ের কোড কপি করে শুধুমাত্র ইমেইল, পাসওয়ার্ড এবং একটি সাবমিট বাটন রেখে লগইন পেজ ডিজাইন করা।
  • Authentication Cookie: SignInManager সফলভাবে লগইন করালে এটি ব্রাউজারে একটি aspnetcore.identity.application কুকি সেট করে। এই কুকি যতক্ষণ ব্রাউজারে থাকে, ইউজারকে লগইন ধরা হয়।
  • Login Action (POST): _signInManager.PasswordSignInAsync() মেথড ব্যবহার করে ডাটাবেস চেক করা এবং ইউজারকে লগইন করানো।
  • Logout Action: _signInManager.SignOutAsync() কল করে ব্রাউজার থেকে আইডেন্টিটি কুকি ডিলিট করে ইউজারকে লগআউট করানো।

🧠 Comprehensive Breakdown

এখানে লেকচারের প্রতিটি বিষয় বিস্তারিতভাবে এবং ধাপে ধাপে ব্যাখ্যা করা হলো:

লগইন ফিচার তৈরি করার আগে বুঝতে হবে ব্যাকগ্রাউন্ডে কী ঘটে। যখন একজন ইউজার সঠিক ইমেইল ও পাসওয়ার্ড দেয়, সার্ভার (ASP.NET Core) একটি Identity Cookie তৈরি করে ব্রাউজারে পাঠিয়ে দেয়। সার্ভার নিজে এই কুকি মেমোরিতে সেভ করে রাখে না (Stateless)।

পরবর্তীতে ইউজার যখন অন্য কোনো পেজে (যেমন: /persons/index) রিকোয়েস্ট করে, ব্রাউজার অটোমেটিকভাবে রিকোয়েস্টের সাথে সেই কুকিটি সার্ভারে পাঠায়। সার্ভার সেই কুকি ডিক্রিপ্ট করে বুঝতে পারে যে ইউজার লগইন করা আছে। লগআউট করার অর্থ হলো সার্ভার ব্রাউজারকে নির্দেশ দেয় সেই কুকিটি ডিলিট করে ফেলার জন্য।

২. Creating LoginDTO (Priority: 8/10)

লগইন ফর্মের জন্য ইউজারের কাছ থেকে শুধু দুটি ডেটা দরকার: Email (যাকে আমরা Username হিসেবে ব্যবহার করছি) এবং Password। তাই Core প্রজেক্টের DTO ফোল্ডারে LoginDTO তৈরি করতে হবে।

💻 Code Implementation:

using System.ComponentModel.DataAnnotations;
 
namespace ContactManager.Core.DTO
{
    public class LoginDTO
    {
        [Required(ErrorMessage = "Email can't be blank")]
        [EmailAddress(ErrorMessage = "Email should be in a proper valid email format")]
        public string Email { get; set; } // Username হিসেবে ব্যবহার হবে
 
        [Required(ErrorMessage = "Password can't be blank")]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}
 

৩. Creating Login View (Priority: 9/10)

Views/Account ফোল্ডারে Login.cshtml ফাইল তৈরি করুন। লেকচারারের মতে, আপনি Register ভিউয়ের কোড কপি-পেস্ট করে অপ্রয়োজনীয় ফিল্ডগুলো (Name, Phone Number, Confirm Password) ডিলিট করে দিলেই কাজ দ্রুত হবে।

💻 Code Implementation:

@model LoginDTO
 
@{
    ViewBag.Title = "Login";
    ViewBag.CurrentUrl = "~/Account/Login";
}
 
<div class="text-large">Login</div>
 
<form asp-controller="Account" asp-action="Login" method="post">
    <div>
        <label asp-for="Email"></label>
        <input asp-for="Email" class="form-control" />
        <span asp-validation-for="Email" class="text-danger"></span>
    </div>
 
    <div>
        <label asp-for="Password"></label>
        <input asp-for="Password" class="form-control" />
        <span asp-validation-for="Password" class="text-danger"></span>
    </div>
 
    <button type="submit" class="btn btn-primary">Login</button>
    <div asp-validation-summary="All" class="text-danger"></div>
</form>
 

৪. Login Action Methods in Controller (Priority: 10/10)

এখন AccountController-এ লগইন পেজ দেখানোর জন্য GET এবং ডেটা ভেরিফাই করার জন্য POST মেথড তৈরি করতে হবে।

Why PasswordSignInAsync? আমরা যখন Register করেছিলাম, তখন SignInAsync ব্যবহার করেছিলাম কারণ তখন ইউজারের অবজেক্টটি সদ্য তৈরি করা হয়েছিল। কিন্তু লগইনের সময় আমাদের কাছে শুধু ইমেইল ও প্লেইন পাসওয়ার্ড থাকে। PasswordSignInAsync ডাটাবেস থেকে ইমেইল অনুযায়ী হ্যাশ (Hash) করা পাসওয়ার্ড বের করে এবং ইউজারের দেওয়া পাসওয়ার্ডের সাথে মিলিয়ে দেখে।

💻 Code Implementation:

// GET: ফর্ম দেখানোর জন্য
[HttpGet]
public IActionResult Login()
{
    return View();
}
 
// POST: লগইন প্রসেস করার জন্য
[HttpPost]
public async Task<IActionResult> Login(LoginDTO loginDTO)
{
    if (!ModelState.IsValid)
    {
        return View(loginDTO); // Validation ফেইল করলে আবার ফর্ম দেখাবে
    }
 
    // SignInManager ব্যবহার করে Authentication
    // Parameters: userName(Email), password, isPersistent, lockoutOnFailure
    var result = await _signInManager.PasswordSignInAsync(
        loginDTO.Email, 
        loginDTO.Password, 
        isPersistent: false, 
        lockoutOnFailure: false);
 
    if (result.Succeeded)
    {
        // লগইন সফল হলে রিডাইরেক্ট করবে
        return RedirectToAction(nameof(Index), "Persons");
    }
    else
    {
        // ইমেইল বা পাসওয়ার্ড ভুল হলে কাস্টম Error Message সেট করা হচ্ছে
        ModelState.AddModelError("Login", "Invalid email or password.");
        return View(loginDTO);
    }
}
 
  • isPersistent: false থাকলে ব্রাউজার ক্লোজ করলেই ইউজার লগআউট হয়ে যাবে (Session Cookie)। true দিলে ব্রাউজার ক্লোজ করার পরও লগইন থাকবে।
  • lockoutOnFailure: true দিলে ইউজার বারবার ভুল পাসওয়ার্ড দিলে সিকিউরিটির জন্য অ্যাকাউন্ট কিছুক্ষণের জন্য লক হয়ে যাবে।

৫. Logout Action Method (Priority: 10/10)

লগআউট করার জন্য SignInManager-এর SignOutAsync() মেথড ব্যবহার করতে হয়। এটি ব্রাউজারকে নির্দেশ দেয় Authentication Cookie ডিলিট করে ফেলার জন্য।

💻 Code Implementation:

[HttpGet]
public async Task<IActionResult> Logout()
{
    await _signInManager.SignOutAsync();
    return RedirectToAction(nameof(Index), "Persons");
}
 

(নোট: লেকচারার এখানে HttpGet ব্যবহার করেছেন, কিন্তু রিয়েল ওয়ার্ল্ডে এটি একটি সিকিউরিটি রিস্ক। নিচে Best Practices অংশে আমি এর সমাধান দিয়েছি।)


🌟 Best Practices & Modern Updates (.NET 10 Context)

  • CRITICAL Security Issue (GET Logout): লেকচারার Logout-এর জন্য [HttpGet] ব্যবহার করেছেন। এটি একটি মারাত্মক Anti-Pattern। হ্যাকাররা ক্রস-সাইট অ্যাটাক (CSRF) এর মাধ্যমে ইমেজ ট্যাগের ভেতর আপনার লগআউট লিংকের URL বসিয়ে ইউজারকে অটোমেটিক লগআউট করিয়ে দিতে পারে।
  • The Fix: সবসময় Logout এর জন্য [HttpPost] ব্যবহার করবেন।
// .NET 10 Best Practice
[HttpPost]
[ValidateAntiForgeryToken] // CSRF প্রতিরোধের জন্য
public async Task<IActionResult> Logout()
{
    await _signInManager.SignOutAsync();
    return RedirectToAction(nameof(Index), "Persons");
}
 

এবং UI-তে <a> ট্যাগের বদলে একটি <form> ব্যবহার করবেন:

<form asp-controller="Account" asp-action="Logout" method="post">
    <button type="submit" class="nav-link btn btn-link">Logout</button>
</form>
 
  • Use [AllowAnonymous]: লগইন এবং রেজিস্টার মেথডগুলোর উপরে [AllowAnonymous] অ্যাট্রিবিউট ব্যবহার করা একটি বেস্ট প্র্যাকটিস। কারণ আপনার পুরো কন্ট্রোলার বা অ্যাপ্লিকেশন যদি পাসওয়ার্ড প্রোটেক্টেড ([Authorize]) থাকে, তাহলেও যেন ইউজার অন্তত লগইন পেজটা অ্যাক্সেস করতে পারে।
  • Generic Error Messages: “Invalid email or password” মেসেজটি সবচেয়ে স্ট্যান্ডার্ড। কখনো “User not found” বা “Incorrect password” আলাদাভাবে বলবেন না। এতে হ্যাকাররা বুঝতে পারে যে ডাটাবেসে ওই ইমেইলটি আদৌ আছে কিনা।

দারুণ কাজ করছেন! আপনার প্রজেক্ট এখন সম্পূর্ণ Authentication সিস্টেমের অধীনে চলে এসেছে। পরবর্তী লেকচারগুলোতে আমরা Role (যেমন: Admin, Normal User) এবং Authorization নিয়ে কাজ শুরু করবো!