using System.IdentityModel.Tokens.Jwt; using System.Net; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using Microsoft.IdentityModel.Tokens; using MySql.Data.MySqlClient; using Newtonsoft.Json; namespace Server; public class Login : SecuredRoute { private static void ValidateParams(Dictionary paramsToValidate) { if ( string.IsNullOrEmpty(paramsToValidate["email"]) || string.IsNullOrEmpty(paramsToValidate["password"]) ) throw new Exception("Invalid parameters"); } public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response) { try { List bodyParamNames = ["email", "password"]; var bodyParamValues = ExtractBody(request, bodyParamNames); ValidateParams(bodyParamValues); string query = @"SELECT id, password FROM user WHERE email=@email;"; MySqlCommand cmd = new(query); cmd.Parameters.AddWithValue("@email", bodyParamValues["email"]); var userId = ExtractUserIdFromDB(cmd, bodyParamValues["password"]); string? jsonResponse = JsonConvert.SerializeObject(GenerateToken(userId)); // prepare response SendSuccess(response, jsonResponse); } catch (Exception ex) { SendError(response, ex); } } private static string ExtractUserIdFromDB(MySqlCommand cmd, string password) { using MySqlConnection conn = new(connectionString); cmd.Connection = conn; conn.Open(); // execute query and read results MySqlDataReader reader = cmd.ExecuteReader(); string? userId = ""; string? hashedPass = ""; while (reader.Read()) { userId = Convert.ToString(reader["id"]); hashedPass = reader.GetString("password"); } // check username if (string.IsNullOrEmpty(userId)) { throw new Exception("Invalid Username or Password"); } //check password if ( string.IsNullOrEmpty(password) || string.IsNullOrEmpty(hashedPass) || !VerifyPassword(password, hashedPass) ) { throw new Exception("Invalid Username or Password"); } return userId; } public static string GenerateToken(string user) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: "TimeLogServer", audience: "TimeLogWebsite", claims: [new Claim("user", user)], expires: DateTime.Now.AddHours(2), signingCredentials: creds ); return new JwtSecurityTokenHandler().WriteToken(token); } public static bool VerifyPassword(string enteredPassword, string storedHash) { byte[] hashBytes = Convert.FromBase64String(storedHash); byte[] salt = new byte[16]; Array.Copy(hashBytes, 0, salt, 0, 16); using var pbkdf2 = new Rfc2898DeriveBytes( enteredPassword, salt, 10000, HashAlgorithmName.SHA256 ); byte[] newHash = pbkdf2.GetBytes(32); for (int i = 0; i < 32; i++) { if (newHash[i] != hashBytes[i + 16]) return false; } return true; } }