Authentication, JWT Tokens & Password Hashing Masterclass

โšก C# 12 & .NET 8 ๐ŸŸข Chapter 39 of 35 ๐Ÿ“‚ Phase 14: Authentication & Security ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this chapter: Auth vs Authorization ยท User Registration ยท BCrypt Password Hashing ยท JWT Token ยท Claims ยท JwtSecurityToken ยท Bearer Authentication ยท Refresh Tokens ยท Logout

Welcome to Phase 14 (Chapter 39): C# Authentication โ€” JWT Tokens, Password Hashing, Claims & Refresh Tokens Masterclass! Authentication verifies who the user is. In this chapter, we implement a complete auth system: user registration with BCrypt password hashing, login with JWT (JSON Web Token) generation, Claims-based identity, Refresh tokens for session extension, and token validation middleware.

1Authentication vs Authorization
ConceptQuestion AnsweredMechanism
Authentication"Who are you?" โ€” Proves the user's identity.Login with username+password โ†’ JWT Token issued
Authorization"What can you do?" โ€” Controls what an authenticated user can access.[Authorize(Roles = "Admin")] attribute on endpoints
2Password Hashing with BCrypt
Terminal & C# โ€” BCrypt Password Hashing โ–ถ Run in Compiler
// Install BCrypt package
// dotnet add package BCrypt.Net-Next

// User Entity
public class User
{
    public int    Id           { get; set; }
    public string Email        { get; set; } = "";
    public string PasswordHash { get; set; } = ""; // NEVER store plaintext!
    public string Role         { get; set; } = "User";
}

// AuthService โ€” Registration
public async Task<User> RegisterAsync(string email, string password)
{
    if (await _context.Users.AnyAsync(u => u.Email == email))
        throw new InvalidOperationException("Email already registered.");

    var user = new User
    {
        Email        = email.ToLower(),
        PasswordHash = BCrypt.Net.BCrypt.HashPassword(password), // Salted hash
        Role         = "User"
    };

    _context.Users.Add(user);
    await _context.SaveChangesAsync();
    return user;
}

// Login โ€” Verify password
public bool VerifyPassword(string inputPassword, string storedHash)
    => BCrypt.Net.BCrypt.Verify(inputPassword, storedHash);
3JWT Token Generation & Validation
C# โ€” JWT Token Generation โ–ถ Run in Compiler
// dotnet add package System.IdentityModel.Tokens.Jwt
// dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

public string GenerateJwtToken(User user)
{
    var jwtKey = _config["Jwt:Key"] ?? throw new InvalidOperationException("JWT Key not configured!");
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

    var claims = new[]
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Email, user.Email),
        new Claim(ClaimTypes.Role, user.Role)
    };

    var token = new JwtSecurityToken(
        issuer:   _config["Jwt:Issuer"],
        audience: _config["Jwt:Audience"],
        claims:   claims,
        expires:  DateTime.UtcNow.AddHours(1),
        signingCredentials: creds
    );

    return new JwtSecurityTokenHandler().WriteToken(token);
}

// Program.cs โ€” Configure JWT Authentication Middleware
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)),
            ValidateIssuer   = true,
            ValidIssuer      = builder.Configuration["Jwt:Issuer"],
            ValidateAudience = true,
            ValidAudience    = builder.Configuration["Jwt:Audience"],
            ValidateLifetime = true
        };
    });
4Technical FAQs

Q1: What is a JWT Refresh Token?

A Refresh Token is a long-lived, opaque token stored securely (in an HttpOnly cookie or database) used to obtain new short-lived JWT Access Tokens after they expire. This avoids forcing users to re-login while keeping Access Tokens short-lived for security.

Q2: Why is BCrypt preferred over MD5/SHA1 for passwords?

BCrypt automatically salts and hashes passwords and has a configurable work factor that makes brute-force attacks computationally expensive. MD5/SHA1 are fast hashing algorithms not designed for password storage and are vulnerable to rainbow table attacks.