Barış Kısır

Modern Security Architecture: JWT Authentication and Password Hashing in .NET Core with MySQL

14 Aug 2019

The Foundations of Stateless Identity

A JSON Web Token (JWT) is a standardized, compact method (RFC 7519) for securely transmitting information as a JSON object. In modern distributed systems, JWTs are the preferred mechanism for stateless authentication, allowing services to verify user identity without maintaining localized session state.

Anatomy of a JWT

A JWT is a base64url encoded string consisting of three cryptographically bound segments:

  1. Header: Defines the signing algorithm (e.g., HS256) and token type.
  2. Payload: Contains Claims—assertions about the user (e.g., ID, Role, Email) and technical metadata like expiration (exp).
  3. Signature: A cryptographic hash of the header and payload, generated using a server-side secret key to ensure data integrity.

Strategic Implementation: Generating Secure Tokens

The following implementation demonstrates how to orchestrate a login workflow that validates credentials and issues a signed JWT.

[AllowAnonymous]
[HttpPost("authenticate")]
public ActionResult<AuthResponse> Login([FromBody] LoginRequest request)
{
    using (var context = new ApplicationDbContext())
    {
        var user = context.Users.SingleOrDefault(u => u.Email == request.Email);
        if (user != null && CryptoUtility.VerifySecureHash(request.Password, user.Salt, user.HashedPassword))
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_config["Jwt:Secret"]);
            
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new[] {
                    new Claim(ClaimTypes.Name, user.Email),
                    new Claim(ClaimTypes.Role, user.Role)
                }),
                Expires = DateTime.UtcNow.AddHours(2),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            return Ok(new AuthResponse { Token = tokenHandler.WriteToken(token) });
        }
    }
    return Unauthorized("Authentication failed: Invalid credentials.");
}

Credential Hardening: Salted Hashing in .NET

Raw SHA-based hashing is insufficient against modern dictionary and rainbow table attacks. We implement a robust defense by generating a unique, cryptographically random Salt for every user and executing iterative hashing.

public static class CryptoUtility
{
    public static string GenerateSalt() => Guid.NewGuid().ToString("N");

    public static string ComputeIterativeHash(string password, string salt)
    {
        var hash = password + salt;
        for (int i = 0; i < 10000; i++) // Standard iteration count for computational cost
        {
            hash = BitConverter.ToString(SHA512.Create().ComputeHash(Encoding.UTF8.GetBytes(hash)));
        }
        return hash;
    }
}

Configuring the Middleware Pipeline

In the .NET Core startup sequence, we inject the authentication handlers to intercept and validate incoming Bearer tokens.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(x => {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(x => {
        x.RequireHttpsMetadata = true;
        x.SaveToken = true;
        x.TokenValidationParameters = new TokenValidationParameters {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:Secret"])),
            ValidateIssuer = false,
            ValidateAudience = false
        };
    });
}

Data Persistence: MySQL Integration

For high-performance data storage, MySQL is utilized via the Pomelo.EntityFrameworkCore.MySql provider, facilitating seamless EF Core migrations and strongly-typed queries.

-- Optimized User Schema
CREATE TABLE `Users` (
  `Id` int NOT NULL AUTO_INCREMENT,
  `Email` varchar(255) NOT NULL,
  `Salt` char(32) NOT NULL,
  `HashedPassword` varchar(512) NOT NULL,
  `Role` varchar(50) DEFAULT 'User',
  PRIMARY KEY (`Id`),
  UNIQUE KEY `Email_UNIQUE` (`Email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Advanced Security Best Practices

  1. Secret Rotation: Never hard-code your secret keys. Utilize environment variables or Azure Key Vault.
  2. HTTPS Enforcement: JWTs are susceptible to man-in-the-middle attacks if transmitted over unencrypted channels.
  3. Claims Sensitivity: Do not include sensitive information (like passwords or PII) in the JWT payload, as it is easily decoded by third parties.

Deep Dive: Explore the full source code and database migrations on GitHub.