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

কোথায় আছি আমরা? আপনি বর্তমানে Section 25: Identity-এর একটি অ্যাডভান্সড টপিকে আছেন। গত লেকচারে আমরা Role-Based Authentication শিখেছিলাম। কিন্তু মাঝে মাঝে আমাদের আরও জটিল কন্ডিশন চেক করতে হয়, যেমন: “যদি ইউজার আগে থেকেই লগইন করা থাকে, তবে তাকে কোনোভাবেই আর Login বা Register পেজে ঢুকতে দেওয়া যাবে না।” আজকে আমরা শিখবো Custom Authorization Policies ব্যবহার করে কীভাবে এই ধরনের লজিক ইমপ্লিমেন্ট করতে হয়। চলুন শুরু করি!


📝 Quick Summary for Revision

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

  • Custom Policy: শুধুমাত্র Role দিয়ে কাজ না হলে আমরা নিজেদের মতো কন্ডিশন বা রুলস (Policy) তৈরি করতে পারি।
  • AddPolicy: AddAuthorization সার্ভিসের ভেতরে options.AddPolicy ব্যবহার করে নতুন Policy রেজিস্টার করতে হয়।
  • RequireAssertion: এর মাধ্যমে Lambda expression ব্যবহার করে কাস্টম লজিক (যেমন: ইউজার অথেনটিকেটেড না হলেই শুধু true রিটার্ন করবে) লেখা যায়।
  • Applying Policy: Controller বা Action Method-এর ওপর [Authorize(Policy = "PolicyName")] লিখে এটি অ্যাপ্লাই করা যায়।
  • Conflict with AllowAnonymous: [AllowAnonymous] অ্যাট্রিবিউট যেকোনো Authorization রুলকে বাইপাস (override) করে দেয়। তাই Custom Policy কাজ করানোর জন্য Controller থেকে [AllowAnonymous] রিমুভ করতে হবে।

🧠 Comprehensive Breakdown

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

১. Why Custom Authorization Policies? (Priority: 9/10)

সাধারণত Role-based authorization (যেমন: [Authorize(Roles = "Admin")]) দিয়ে বেশিরভাগ কাজ হয়ে যায়। কিন্তু কিছু স্পেশাল সিনারিও থাকে। যেমন, একজন ইউজার অলরেডি লগইন করা আছে। এখন সে যদি ব্রাউজারের URL বারে গিয়ে /Account/Login বা /Account/Register লেখে, তবে তাকে আবার লগইন পেজ দেখানোটা লজিক্যাল নয়। আমরা চাই সে লগইন অবস্থায় থাকলে যেন এই পেজগুলোতে এক্সেস না পায়। এই ধরনের কাস্টম লজিক ইমপ্লিমেন্ট করার জন্যই Custom Authorization Policy ব্যবহার করা হয়।

২. Creating a Custom Policy (Priority: 10/10)

আমাদের একটি নতুন Policy তৈরি করতে হবে যা চেক করবে বর্তমান ইউজার লগইন করা আছে কিনা। যদি লগইন না থাকে, তবেই সে অ্যাক্সেস পাবে।

💻 Code Implementation (StartupExtensions.cs বা Program.cs): যেখানে আমরা AddAuthorization কনফিগার করেছিলাম, সেখানে এই নতুন Policy যুক্ত করতে হবে।

services.AddAuthorization(options =>
{
    // আগের Fallback Policy...
    
    // নতুন কাস্টম পলিসি তৈরি করা হচ্ছে
    options.AddPolicy("NotAuthorized", policy =>
    {
        // RequireAssertion এর মাধ্যমে কাস্টম লজিক লেখা যায়
        policy.RequireAssertion(context =>
        {
            // যদি ইউজার লগইন করা না থাকে (!), তবেই true রিটার্ন করবে (মানে অ্যাক্সেস পাবে)
            return !context.User.Identity.IsAuthenticated;
        });
    });
});
 

৩. Applying the Policy to Action Methods (Priority: 10/10)

এখন আমাদের AccountController-এর Login এবং Register অ্যাকশন মেথডগুলোতে এই Policy অ্যাপ্লাই করতে হবে।

💻 Code Implementation (AccountController.cs):

// Login GET Method
[HttpGet]
[Authorize(Policy = "NotAuthorized")] // কাস্টম পলিসি অ্যাপ্লাই করা হলো
public IActionResult Login()
{
    return View();
}
 
// Login POST Method
[HttpPost]
[Authorize(Policy = "NotAuthorized")]
public async Task<IActionResult> Login(LoginDTO loginDTO, string? returnUrl)
{
    // ... Login Logic ...
}
 
// Register GET এবং POST মেথডেও একইভাবে [Authorize(Policy = "NotAuthorized")] বসাতে হবে।
 

৪. Securing the Logout Method (Priority: 8/10)

Logout মেথডটি সম্পূর্ণ বিপরীত! কেউ লগআউট করতে চাইলে তাকে অবশ্যই আগে লগইন করা থাকতে হবে। তাই এর ওপর আমাদের কাস্টম পলিসি বসবে না, বরং রেগুলার [Authorize] বসবে।

[HttpGet]
[Authorize] // শুধুমাত্র লগইন করা ইউজাররাই লগআউট করতে পারবে
public async Task<IActionResult> Logout()
{
    await _signInManager.SignOutAsync();
    return RedirectToAction(nameof(Index), "Persons");
}
 

৫. The Conflict: Removing [AllowAnonymous] (Priority: 10/10)

লেকচারার কোড রান করার পর দেখেন যে Policy কাজ করছে না, লগইন থাকা সত্ত্বেও ইউজার Login পেজে ঢুকে যাচ্ছে! কারণ কী? কারণ AccountController-এর একেবারে ওপরে [AllowAnonymous] লেখা ছিল।

  • The Rule: [AllowAnonymous] সবসময় যেকোনো [Authorize] বা Policy-কে ওভাররাইড করে বা পাত্তা দেয় না। এটি সবাইকে আনকন্ডিশনাল অ্যাক্সেস দিয়ে দেয়।
  • The Fix: Custom Policy ঠিকমতো কাজ করানোর জন্য Controller-এর ওপর থেকে [AllowAnonymous] মুছে ফেলতে হবে।

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

  • Better UX for Logged-In Users (Crucial Best Practice): লেকচারার দেখিয়েছেন যে, লগইন করা ইউজার Login পেজে যেতে চাইলে তাকে AccessDenied পেজে পাঠিয়ে দেওয়া হয়। কিন্তু রিয়েল-ওয়ার্ল্ড ইউজার এক্সপেরিয়েন্সের (UX) দিক থেকে এটি ভালো প্র্যাকটিস নয়। একজন ইউজার ভুল করে Login লিংকে ক্লিক করলে তাকে “Access Denied” এরর দেখানোটা রুড (rude) দেখায়।
  • Smarter Solution: Custom Policy ব্যবহার না করে Login GET মেথডের ভেতরেই একটি if কন্ডিশন দিয়ে ইউজারকে ড্যাশবোর্ডে রিডাইরেক্ট করা উচিত।
[HttpGet]
[AllowAnonymous]
public IActionResult Login()
{
    if (User.Identity.IsAuthenticated)
    {
        // লগইন করা থাকলে সোজা ড্যাশবোর্ডে পাঠিয়ে দিন!
        return RedirectToAction("Index", "Persons"); 
    }
    return View();
}
 

(নোট: কোর্সের পারপাস অনুযায়ী Custom Policy শেখার জন্য লেকচারারের পদ্ধতিটি দারুণ, কিন্তু প্রোডাকশনে উপরের লজিকটি বেশি ইউজার-ফ্রেন্ডলি)।

  • Avoid Magic Strings: "NotAuthorized" নামটি একটি Magic String (হার্ডকোডেড)। ভুল করে বানান ভুল হলে অ্যাপ্লিকেশন ক্র্যাশ করবে (যেমনটা লেকচারারের হয়েছিলো)। একটি Constants ক্লাস তৈরি করে সেখানে Policy-র নামগুলো স্টোর করে রাখা Best Practice।
public static class PolicyNames 
{
    public const string UnauthenticatedUser = "UnauthenticatedUserPolicy";
}
 

অসাধারণ! আপনি ASP.NET Core Identity এর বেসিক থেকে শুরু করে একেবারে অ্যাডভান্সড Authorization Policies পর্যন্ত দারুণভাবে কভার করেছেন। আপনার অ্যাপ্লিকেশন এখন সিকিউরিটির দিক থেকে বেশ শক্তিশালী!