From 05f09e57ff88d64715edef35a87034ea179ce855 Mon Sep 17 00:00:00 2001 From: Jeas0001 Date: Wed, 19 Mar 2025 13:22:39 +0100 Subject: [PATCH] =?UTF-8?q?JWT=20tokens=20til=20n=C3=A5r=20du=20logger=20i?= =?UTF-8?q?nd=20og=20passwword=20bliver=20hashet=20nu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/Api/Api.csproj | 1 + backend/Api/Controllers/DeviceController.cs | 2 +- backend/Api/Controllers/UserController.cs | 45 +++++++++++++- backend/Api/DBAccess/DBAccess.cs | 69 +++++++++++++++++---- backend/Api/Models/User.cs | 2 + backend/Api/Startup.cs | 28 ++++++++- 6 files changed, 129 insertions(+), 18 deletions(-) diff --git a/backend/Api/Api.csproj b/backend/Api/Api.csproj index 3e02616..71bca14 100644 --- a/backend/Api/Api.csproj +++ b/backend/Api/Api.csproj @@ -10,6 +10,7 @@ + all diff --git a/backend/Api/Controllers/DeviceController.cs b/backend/Api/Controllers/DeviceController.cs index a697cf6..5c53c3f 100644 --- a/backend/Api/Controllers/DeviceController.cs +++ b/backend/Api/Controllers/DeviceController.cs @@ -15,7 +15,7 @@ namespace Api.Controllers _context = context; } - + // For at få json webtokens til at virke skriv [Authorize] over de endpoints [HttpGet] public async Task GetDevices(int userId) { diff --git a/backend/Api/Controllers/UserController.cs b/backend/Api/Controllers/UserController.cs index 58129e1..8a9077a 100644 --- a/backend/Api/Controllers/UserController.cs +++ b/backend/Api/Controllers/UserController.cs @@ -1,6 +1,10 @@ using Microsoft.AspNetCore.Mvc; using Api.Models; using Api.DBAccess; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; namespace Api.Controllers { @@ -9,10 +13,12 @@ namespace Api.Controllers public class UserController : Controller { private readonly DBContext _context; + private readonly IConfiguration _configuration; - public UserController(DBContext context) + public UserController(DBContext context, IConfiguration configuration) { _context = context; + _configuration = configuration; } [HttpPost("Login")] @@ -20,8 +26,9 @@ namespace Api.Controllers { DbAccess dBAccess = new DbAccess(_context); user = await dBAccess.Login(user); - if (user.Id == 0) { return BadRequest(new { error = "User can't be logged in" }); } - return Ok(user); + if (user.Id == 0) { return Unauthorized(new { error = "Invalid username or password" }); } + var token = GenerateJwtToken(user); + return Ok(new { token, user.UserName, user.Id }); } [HttpPost("Create")] @@ -41,5 +48,37 @@ namespace Api.Controllers if (!success) { return BadRequest(new { error = "User can't be edited" }); } return Ok(); } + + [HttpDelete("Delete/{userId}")] + public async Task DeleteUser(int userId) + { + DbAccess dbAccess = new DbAccess(_context); + bool success = await dbAccess.DeleteUser(userId); + if (!success) { return BadRequest(new { error = "User can't be deleted" }); } + return Ok(); + } + + private string GenerateJwtToken(User user) + { + var claims = new[] + { + new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim(ClaimTypes.Name, user.UserName) + }; + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes + (_configuration["JwtSettings:Key"])); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var token = new JwtSecurityToken( + _configuration["JwtSettings:Issuer"], + _configuration["JwtSettings:Audience"], + claims, + expires: DateTime.Now.AddMinutes(30), + signingCredentials: creds); + + return new JwtSecurityTokenHandler().WriteToken(token); + } } } diff --git a/backend/Api/DBAccess/DBAccess.cs b/backend/Api/DBAccess/DBAccess.cs index 2a19f1d..b4c803a 100644 --- a/backend/Api/DBAccess/DBAccess.cs +++ b/backend/Api/DBAccess/DBAccess.cs @@ -1,5 +1,8 @@ using Microsoft.EntityFrameworkCore; using Api.Models; +using System.Text; +using System.Runtime.Intrinsics.Arm; +using System.Security.Cryptography; namespace Api.DBAccess @@ -28,6 +31,11 @@ namespace Api.DBAccess { user.Devices = new List(); } + string salt = Guid.NewGuid().ToString(); + string hashedPassword = ComputeHash(user.Password, SHA256.Create(), salt); + + user.Salt = salt; + user.Password = hashedPassword; _context.Users.Add(user); return await _context.SaveChangesAsync() == 1; @@ -36,10 +44,11 @@ namespace Api.DBAccess public async Task Login(User user) { var profile = await _context.Users.FirstAsync(u => u.UserName == user.UserName); + + string hashedPassword = ComputeHash(user.Password, SHA256.Create(), profile.Salt); - if (profile.Password == user.Password) + if (hashedPassword == user.Password) { - profile.Password = ""; return profile; } return new User(); @@ -58,6 +67,25 @@ namespace Api.DBAccess return await _context.SaveChangesAsync() == 1; } + public async Task DeleteUser(int userId) + { + var user = await _context.Users.Include(u => u.Devices).FirstOrDefaultAsync(u => u.Id == userId); + if (user != null) + { + if (user.Devices != null && user.Devices.Count > 0) + { + foreach (var item in user.Devices) + { + var device = await _context.Devices.Include(d => d.Logs).FirstOrDefaultAsync(d => d.Id == item.Id); + if (device != null) { _context.Devices.Remove(device); } + } + } + _context.Users.Remove(user); + return await _context.SaveChangesAsync() == 1; + } + return false; + } + public async Task> ReadDevices(int userId) { var user = await _context.Users.Include(u => u.Devices).FirstOrDefaultAsync(u => u.Id == userId); @@ -82,17 +110,6 @@ namespace Api.DBAccess return await _context.SaveChangesAsync() == 1; } - public async Task> ReadLogs(int deviceId) - { - var device = await _context.Devices.Include(d => d.Logs).FirstOrDefaultAsync(d => d.Id == deviceId); - - if (device == null || device.Logs == null) { return new List(); } - - var logs = device.Logs; - - return logs; - } - public async Task UpdateDevice(Device device, int deviceId) { var device1 = await _context.Devices.FirstAsync(u => u.Id == deviceId); @@ -105,5 +122,31 @@ namespace Api.DBAccess return await _context.SaveChangesAsync() == 1; } + + public async Task> ReadLogs(int deviceId) + { + var device = await _context.Devices.Include(d => d.Logs).FirstOrDefaultAsync(d => d.Id == deviceId); + + if (device == null || device.Logs == null) { return new List(); } + + var logs = device.Logs; + + return logs; + } + + private static string ComputeHash(string input, HashAlgorithm algorithm, string salt) + { + Byte[] inputBytes = Encoding.UTF8.GetBytes(input); + Byte[] saltBytes = Encoding.UTF8.GetBytes(salt); + + // Combine salt and input bytes + Byte[] saltedInput = new Byte[saltBytes.Length + inputBytes.Length]; + saltBytes.CopyTo(saltedInput, 0); + inputBytes.CopyTo(saltedInput, saltBytes.Length); + + Byte[] hashedBytes = algorithm.ComputeHash(saltedInput); + + return BitConverter.ToString(hashedBytes); + } } } diff --git a/backend/Api/Models/User.cs b/backend/Api/Models/User.cs index 20e495b..fd519ba 100644 --- a/backend/Api/Models/User.cs +++ b/backend/Api/Models/User.cs @@ -10,6 +10,8 @@ public string Email { get; set; } + public string Salt { get; set; } + public List? Devices { get; set; } } } diff --git a/backend/Api/Startup.cs b/backend/Api/Startup.cs index 3429ea3..be08ce2 100644 --- a/backend/Api/Startup.cs +++ b/backend/Api/Startup.cs @@ -1,5 +1,8 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; +using System.Text; namespace Api { @@ -22,6 +25,29 @@ namespace Api services.AddControllers(); + services.AddAuthentication(x => + { + x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }).AddJwtBearer(x => + { + x.TokenValidationParameters = new TokenValidationParameters + { + ValidIssuer = _configuration["JwtSettings:Issuer"], + ValidAudience = _configuration["JwtSettings:Audience"], + IssuerSigningKey = new SymmetricSecurityKey + ( + Encoding.UTF8.GetBytes(_configuration["JwtSettings:Key"]) + ), + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true + }; + }); + + services.AddCors(options => { options.AddPolicy("AllowAll", builder =>