Implementing Cookie Authentication Scheme in ASP.NET Core Refined

Cookie Authentication in Razor Pages

हमने पिछली पोस्ट में देखा कि साइन-इन प्रक्रिया के दौरान ऑथेंटिकेशन कुकी कैसे बनाई जाती है। उस उदाहरण में हमने एक निश्चित (fixed) यूज़रनेम और पासवर्ड का उपयोग किया था, जो वास्तविक एप्लीकेशन की तुलना में काफी सरल था। वह केवल ऑथेंटिकेशन कुकी की अवधारणा समझाने के लिए किया गया था। अब हम यह समझेंगे कि वास्तविक एप्लीकेशन में प्रत्येक यूज़र के यूज़रनेम और पासवर्ड को सुरक्षित तरीके से कैसे संग्रहीत (store) किया जाता है, तथा साइन-इन के समय उनका मिलान (validate) कैसे किया जाता है। वास्तविक एप्लीकेशन में यूज़र की जानकारी को किसी डेटाबेस (जैसे SQL Server, SQLite आदि) या फ़ाइल (जैसे CSV या JSON) में सुरक्षित रूप से रखा जाता है। इस उदाहरण में हम सरलता के लिए CSV फ़ाइल का उपयोग करेंगे।

महत्वपूर्ण सुरक्षा बिंदु

  • यूज़र की जानकारी को web root (wwwroot) के अंदर नहीं रखना चाहिए।
  • क्योंकि web root के अंदर की फ़ाइलें सीधे ब्राउज़र से एक्सेस की जा सकती हैं।
  • इसलिए CSV फ़ाइल को wwwroot के बाहर रखा जाएगा।

कार्यप्रवाह (Flow)

1. यूज़र फॉर्म भरकर रजिस्टर करता है → डेटा CSV फ़ाइल में सेव होता है 
2. यूज़र लॉगिन करता है → CSV से डेटा पढ़कर मिलान किया जाता है 
3. सही होने पर → Authentication Cookie बनाई जाती है

Razor Page Form (Login/Register)


<form method="post">
    <label>Username:</label>
    <input type="text" name="Username" required />

    <label>Password:</label>
    <input type="password" name="Password" required />

    <button type="submit" name="action" value="register">Register</button>
    <button type="submit" name="action" value="login">Login</button>
</form>

PageModel (C# Code)


using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Text;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

public class IndexModel : PageModel
{
    private readonly string filePath = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "users.csv");

    [BindProperty]
    public string Username { get; set; }

    [BindProperty]
    public string Password { get; set; }

    public void OnGet() { }

    public async Task<IActionResult> OnPostAsync(string action)
    {
        EnsureFileExists();

        if (action == "register")
        {
            RegisterUser(Username, Password);
            return Content("User Registered Successfully");
        }

        if (action == "login")
        {
            if (ValidateUser(Username, Password))
            {
                await SignInUser(Username);
                return Content("Login Successful");
            }
            else
            {
                return Content("Invalid Username or Password");
            }
        }

        return Page();
    }

    private void EnsureFileExists()
    {
        var dir = Path.GetDirectoryName(filePath);
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }

        if (!System.IO.File.Exists(filePath))
        {
            System.IO.File.Create(filePath).Close();
        }
    }

    private void RegisterUser(string username, string password)
    {
        var line = $"{username},{password}";
        System.IO.File.AppendAllText(filePath, line + Environment.NewLine);
    }

    private bool ValidateUser(string username, string password)
    {
        var lines = System.IO.File.ReadAllLines(filePath);

        foreach (var line in lines)
        {
            var parts = line.Split(',');
            if (parts.Length == 2 &&
                parts[0] == username &&
                parts[1] == password)
            {
                return true;
            }
        }

        return false;
    }

    private async Task SignInUser(string username)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, username)
        };

        var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
        var principal = new ClaimsPrincipal(identity);

        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
    }
}

Real-world Best Practices

ऊपर दिया गया कोड समझने के लिए है, लेकिन production में कुछ महत्वपूर्ण सुधार आवश्यक हैं। 
Plain text में password store करना गलत तरीका है। सही तरीका हैं कि Password को hash करके store करें। पासवर्ड को हैश करने के लिए किसी पैकेज का उपयोग किया जा सकता है। आइडेंटिटी पैकेज का उपयोग करके इसे कैसे किया जा सकता है उसका उदाहरण है

PasswordHasher<TUser>

using Microsoft.AspNetCore.Identity;

var hasher = new PasswordHasher<string>();
var hashedPassword = hasher.HashPassword(null, password);
और validate करते समय:
var result = hasher.VerifyHashedPassword(null, storedHash, password);

निष्कर्ष

  1. Authentication cookie यूज़र की पहचान को बनाए रखने के लिए उपयोग होती है
  2. यूज़र डेटा को सुरक्षित स्थान (wwwroot के बाहर) में रखना चाहिए। 
  3. CSV फ़ाइल शुरुआती सीखने के लिए ठीक है, लेकिन production में database उपयोग करना चाहिए
  4. सबसे महत्वपूर्ण: Password को कभी भी plain text में store नहीं करना चाहिए। 

इसी उदाहरण को SQLite या ASP.NET Core Identity के साथ भी पूरी तरह production-ready तरीके से बनाकर आगे इसे बतांएगे। 

टिप्पणियाँ

इस ब्लॉग से लोकप्रिय पोस्ट

Differences between in-process and out-of-process hosting models

Web Fundamental Concepts in Hindi for Beginners - FAQs with their Answers Part-1

Introduction to ASP.NET Core and Web Frameworks