From 751b08e805043ae7caa4945099ad8e279ff145cb Mon Sep 17 00:00:00 2001 From: shept Date: Tue, 25 Mar 2025 19:42:06 +0500 Subject: [PATCH 1/6] added Infrastructure --- SurveyBackend/SurveyBackend.Core/Models/Group.cs | 9 +++++++++ SurveyBackend/SurveyBackend.Core/Models/User.cs | 4 +++- .../SurveyBackend.Infrastructure.csproj | 9 +++++++++ SurveyBackend/SurveyBackend.sln | 6 ++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 SurveyBackend/SurveyBackend.Core/Models/Group.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj diff --git a/SurveyBackend/SurveyBackend.Core/Models/Group.cs b/SurveyBackend/SurveyBackend.Core/Models/Group.cs new file mode 100644 index 0000000..e74cb3f --- /dev/null +++ b/SurveyBackend/SurveyBackend.Core/Models/Group.cs @@ -0,0 +1,9 @@ +namespace SurveyBackend.Core.Models; + +public class Group +{ + public int Id { get; set; } + public string Label { get; set; } + + public ICollection Users { get; set; } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/Models/User.cs b/SurveyBackend/SurveyBackend.Core/Models/User.cs index 509c503..fd8e2fd 100644 --- a/SurveyBackend/SurveyBackend.Core/Models/User.cs +++ b/SurveyBackend/SurveyBackend.Core/Models/User.cs @@ -6,6 +6,8 @@ public class User public string Username { get; set; } public string Email { get; set; } - public byte[] PasswordHash { get; set; } public byte[] PasswordSalt { get; set; } + public byte[] PasswordHash { get; set; } + + public ICollection Groups { get; set; } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/SurveyBackend/SurveyBackend.sln b/SurveyBackend/SurveyBackend.sln index a7701f0..c79d7d1 100644 --- a/SurveyBackend/SurveyBackend.sln +++ b/SurveyBackend/SurveyBackend.sln @@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurveyBackend.API", "Survey EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurveyBackend.Core", "SurveyBackend.Core\SurveyBackend.Core.csproj", "{596B4603-4066-4FF2-9C96-5357193F7229}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurveyBackend.Infrastructure", "SurveyBackend.Infrastructure\SurveyBackend.Infrastructure.csproj", "{4006471D-9F65-4AD6-852B-88A1211B49F4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +20,9 @@ Global {596B4603-4066-4FF2-9C96-5357193F7229}.Debug|Any CPU.Build.0 = Debug|Any CPU {596B4603-4066-4FF2-9C96-5357193F7229}.Release|Any CPU.ActiveCfg = Release|Any CPU {596B4603-4066-4FF2-9C96-5357193F7229}.Release|Any CPU.Build.0 = Release|Any CPU + {4006471D-9F65-4AD6-852B-88A1211B49F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4006471D-9F65-4AD6-852B-88A1211B49F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4006471D-9F65-4AD6-852B-88A1211B49F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4006471D-9F65-4AD6-852B-88A1211B49F4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 6732613466845a426528c8aaf71b3828cdedfa81 Mon Sep 17 00:00:00 2001 From: shept Date: Tue, 25 Mar 2025 22:05:28 +0500 Subject: [PATCH 2/6] idk something about efcore --- .../SurveyBackend.API/DTOs/UserLoginDTO.cs | 7 +++ .../DTOs/UserRegistrationDTO.cs | 10 +++++ .../SurveyBackend.Core/Models/User.cs | 2 + .../Repositories/IGenericRepository.cs | 10 +++++ .../Repositories/IUserRepository.cs | 7 +++ .../Data/DataContext.cs | 15 +++++++ .../Repositories/UserRepository.cs | 44 +++++++++++++++++++ .../SurveyBackend.Infrastructure.csproj | 8 ++++ 8 files changed, 103 insertions(+) create mode 100644 SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs create mode 100644 SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDTO.cs create mode 100644 SurveyBackend/SurveyBackend.Core/Repositories/IGenericRepository.cs create mode 100644 SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs diff --git a/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs b/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs new file mode 100644 index 0000000..a6125a7 --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs @@ -0,0 +1,7 @@ +namespace SurveyBackend.DTOs; + +public record UserLoginDTO +{ + public string Email { get; set; } + public string Password { get; set; } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDTO.cs b/SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDTO.cs new file mode 100644 index 0000000..b3013db --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDTO.cs @@ -0,0 +1,10 @@ +namespace SurveyBackend.DTOs; + +public record UserRegistrationDTO +{ + public string Email { get; set; } + public string Username { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Password { get; set; } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/Models/User.cs b/SurveyBackend/SurveyBackend.Core/Models/User.cs index fd8e2fd..0f57f37 100644 --- a/SurveyBackend/SurveyBackend.Core/Models/User.cs +++ b/SurveyBackend/SurveyBackend.Core/Models/User.cs @@ -3,6 +3,8 @@ namespace SurveyBackend.Core.Models; public class User { public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } public string Username { get; set; } public string Email { get; set; } diff --git a/SurveyBackend/SurveyBackend.Core/Repositories/IGenericRepository.cs b/SurveyBackend/SurveyBackend.Core/Repositories/IGenericRepository.cs new file mode 100644 index 0000000..228d3b1 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Core/Repositories/IGenericRepository.cs @@ -0,0 +1,10 @@ +namespace SurveyBackend.Core.Repositories; + +public interface IGenericRepository where T : class +{ + Task GetByIdAsync(int id); + Task> GetAllAsync(); + Task AddAsync(T entity); + Task UpdateAsync(T entity); + Task DeleteAsync(T entity); +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs b/SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs new file mode 100644 index 0000000..791059a --- /dev/null +++ b/SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs @@ -0,0 +1,7 @@ +using SurveyBackend.Core.Models; + +namespace SurveyBackend.Core.Repositories; + +public interface IUserRepository : IGenericRepository +{ +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs new file mode 100644 index 0000000..26cabc9 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using SurveyBackend.Core.Models; + +namespace SurveyBackend.Infrastructure.Data; + +public class DataContext : DbContext +{ + public DbSet Users { get; set; } + public DbSet Groups { get; set; } + + public DataContext(DbContextOptions options) : base(options) + { + Database.EnsureCreated(); + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs b/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs new file mode 100644 index 0000000..306872e --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using SurveyBackend.Core.Models; +using SurveyBackend.Core.Repositories; +using SurveyBackend.Infrastructure.Data; + +namespace SurveyBackend.Infrastructure.Repositories; + +public class UserRepository : IUserRepository +{ + private readonly DataContext _context; + + public UserRepository(DataContext context) + { + _context = context; + } + + public async Task GetByIdAsync(int id) + { + return await _context.Users.FindAsync(id); + } + + public async Task> GetAllAsync() + { + return await _context.Users.ToListAsync(); + } + + public async Task AddAsync(User entity) + { + await _context.Users.AddAsync(entity); + await _context.SaveChangesAsync(); + } + + public async Task UpdateAsync(User entity) + { + _context.Users.Update(entity); + await _context.SaveChangesAsync(); + } + + public async Task DeleteAsync(User entity) + { + _context.Users.Remove(entity); + await _context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj index 3a63532..eeefcab 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj +++ b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj @@ -6,4 +6,12 @@ enable + + + + + + + + From f9a680c554b61b549900942e2b6e0f1bfe8fa7db Mon Sep 17 00:00:00 2001 From: shept Date: Tue, 25 Mar 2025 22:22:57 +0500 Subject: [PATCH 3/6] started api work --- .../Controllers/AuthController.cs | 15 +++++++++++++++ .../SurveyBackend.API/DTOs/UserLoginDTO.cs | 4 ++-- SurveyBackend/SurveyBackend.API/Program.cs | 6 ++++-- 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs new file mode 100644 index 0000000..94f1cb4 --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc; +using SurveyBackend.DTOs; + +namespace SurveyBackend.Controllers; + +[ApiController] +[Route("[controller]")] +public class AuthController : ControllerBase +{ + [HttpPost("login")] + public async Task GetToken([FromBody] UserLoginDTO loginData) + { + return Ok(); + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs b/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs index a6125a7..9dc2510 100644 --- a/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs +++ b/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs @@ -2,6 +2,6 @@ namespace SurveyBackend.DTOs; public record UserLoginDTO { - public string Email { get; set; } - public string Password { get; set; } + public required string Email { get; set; } + public required string Password { get; set; } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Program.cs b/SurveyBackend/SurveyBackend.API/Program.cs index 3a98e82..e7d098c 100644 --- a/SurveyBackend/SurveyBackend.API/Program.cs +++ b/SurveyBackend/SurveyBackend.API/Program.cs @@ -8,6 +8,8 @@ public class Program // Add services to the container. builder.Services.AddAuthorization(); + + builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); @@ -22,10 +24,10 @@ public class Program app.UseSwaggerUI(); } - app.UseHttpsRedirection(); - app.UseAuthorization(); + app.MapControllers(); + app.Run(); } } \ No newline at end of file From 950babb68c09ea1cc49691d6c794b6437b646c41 Mon Sep 17 00:00:00 2001 From: shept Date: Tue, 25 Mar 2025 22:28:55 +0500 Subject: [PATCH 4/6] Kontur developer assaulted me about using [controller] in route --- SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs index 94f1cb4..b0b0442 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -4,7 +4,7 @@ using SurveyBackend.DTOs; namespace SurveyBackend.Controllers; [ApiController] -[Route("[controller]")] +[Route("auth")] public class AuthController : ControllerBase { [HttpPost("login")] From 35331a87f172f786d08b504da8de9e8773ec352e Mon Sep 17 00:00:00 2001 From: shept Date: Tue, 25 Mar 2025 22:56:35 +0500 Subject: [PATCH 5/6] i'm not sure what i'm doing --- SurveyBackend/SurveyBackend.API/Program.cs | 20 ++++++++++++++++--- .../SurveyBackend.API.csproj | 6 ++++++ .../appsettings.Development.json | 3 +++ .../SurveyBackend.API/appsettings.json | 5 ++++- .../SurveyBackend.Core/Models/User.cs | 10 +++------- .../SurveyBackend.Core.csproj | 5 +++++ .../Data/DataContext.cs | 5 +++-- 7 files changed, 41 insertions(+), 13 deletions(-) diff --git a/SurveyBackend/SurveyBackend.API/Program.cs b/SurveyBackend/SurveyBackend.API/Program.cs index e7d098c..8aa2aca 100644 --- a/SurveyBackend/SurveyBackend.API/Program.cs +++ b/SurveyBackend/SurveyBackend.API/Program.cs @@ -1,3 +1,8 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using SurveyBackend.Core.Models; +using SurveyBackend.Infrastructure.Data; + namespace SurveyBackend; public class Program @@ -8,7 +13,16 @@ public class Program // Add services to the container. builder.Services.AddAuthorization(); - + + builder.Services.AddDbContext(options => + { + options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")); + }); + + builder.Services.AddIdentity>(options => { }) + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle @@ -25,9 +39,9 @@ public class Program } app.UseAuthorization(); - + app.MapControllers(); - + app.Run(); } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj index 41529c3..e11596b 100644 --- a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj +++ b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj @@ -9,7 +9,13 @@ + + + + + + diff --git a/SurveyBackend/SurveyBackend.API/appsettings.Development.json b/SurveyBackend/SurveyBackend.API/appsettings.Development.json index 0c208ae..c14bf00 100644 --- a/SurveyBackend/SurveyBackend.API/appsettings.Development.json +++ b/SurveyBackend/SurveyBackend.API/appsettings.Development.json @@ -4,5 +4,8 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "ConnectionStrings": { + "DefaultConnection": "Data Source=Application.db" } } diff --git a/SurveyBackend/SurveyBackend.API/appsettings.json b/SurveyBackend/SurveyBackend.API/appsettings.json index 10f68b8..19a74e6 100644 --- a/SurveyBackend/SurveyBackend.API/appsettings.json +++ b/SurveyBackend/SurveyBackend.API/appsettings.json @@ -5,5 +5,8 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ConnectionStrings": { + "DefaultConnection": "Data Source=Application.db" + } } diff --git a/SurveyBackend/SurveyBackend.Core/Models/User.cs b/SurveyBackend/SurveyBackend.Core/Models/User.cs index 0f57f37..ab4dcbe 100644 --- a/SurveyBackend/SurveyBackend.Core/Models/User.cs +++ b/SurveyBackend/SurveyBackend.Core/Models/User.cs @@ -1,15 +1,11 @@ +using Microsoft.AspNetCore.Identity; + namespace SurveyBackend.Core.Models; -public class User +public class User : IdentityUser { - public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } - public string Username { get; set; } - public string Email { get; set; } - - public byte[] PasswordSalt { get; set; } - public byte[] PasswordHash { get; set; } public ICollection Groups { get; set; } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj b/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj index 3a63532..7bc422a 100644 --- a/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj +++ b/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj @@ -6,4 +6,9 @@ enable + + + + + diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs index 26cabc9..a44573f 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs @@ -1,11 +1,12 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using SurveyBackend.Core.Models; namespace SurveyBackend.Infrastructure.Data; -public class DataContext : DbContext +public class DataContext : IdentityDbContext, int> { - public DbSet Users { get; set; } public DbSet Groups { get; set; } public DataContext(DbContextOptions options) : base(options) From c2bcaf0832352013e85fc3eaa24c19cbc02a9273 Mon Sep 17 00:00:00 2001 From: shept Date: Tue, 8 Apr 2025 19:08:17 +0500 Subject: [PATCH 6/6] massive work on user auth --- .../Controllers/AuthController.cs | 2 +- .../DTOs/{UserLoginDTO.cs => UserLoginDto.cs} | 2 +- ...istrationDTO.cs => UserRegistrationDto.cs} | 2 +- SurveyBackend/SurveyBackend.API/Program.cs | 31 +++++++--- .../SurveyBackend.API.csproj | 1 + .../appsettings.Development.json | 6 ++ .../SurveyBackend.API/appsettings.json | 6 ++ .../SurveyBackend.Core/Models/User.cs | 6 +- .../Services/IUserService.cs | 6 ++ .../SurveyBackend.Core.csproj | 1 - .../AuthOptions.cs | 32 ++++++++++ ...DataContext.cs => ApplicationDbContext.cs} | 6 +- .../Helpers/TokenHelper.cs | 31 ++++++++++ .../Repositories/UserRepository.cs | 4 +- .../Services/IPasswordHasher.cs | 7 +++ .../Services/Sha256PasswordHasher.cs | 61 +++++++++++++++++++ .../SurveyBackend.Infrastructure.csproj | 1 + 17 files changed, 186 insertions(+), 19 deletions(-) rename SurveyBackend/SurveyBackend.API/DTOs/{UserLoginDTO.cs => UserLoginDto.cs} (82%) rename SurveyBackend/SurveyBackend.API/DTOs/{UserRegistrationDTO.cs => UserRegistrationDto.cs} (87%) create mode 100644 SurveyBackend/SurveyBackend.Core/Services/IUserService.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/AuthOptions.cs rename SurveyBackend/SurveyBackend.Infrastructure/Data/{DataContext.cs => ApplicationDbContext.cs} (59%) create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Helpers/TokenHelper.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Services/IPasswordHasher.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs index b0b0442..93fb89a 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -8,7 +8,7 @@ namespace SurveyBackend.Controllers; public class AuthController : ControllerBase { [HttpPost("login")] - public async Task GetToken([FromBody] UserLoginDTO loginData) + public async Task GetToken([FromBody] UserLoginDto loginData) { return Ok(); } diff --git a/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs b/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDto.cs similarity index 82% rename from SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs rename to SurveyBackend/SurveyBackend.API/DTOs/UserLoginDto.cs index 9dc2510..8e58ee2 100644 --- a/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDTO.cs +++ b/SurveyBackend/SurveyBackend.API/DTOs/UserLoginDto.cs @@ -1,6 +1,6 @@ namespace SurveyBackend.DTOs; -public record UserLoginDTO +public record UserLoginDto { public required string Email { get; set; } public required string Password { get; set; } diff --git a/SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDTO.cs b/SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDto.cs similarity index 87% rename from SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDTO.cs rename to SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDto.cs index b3013db..3c0808d 100644 --- a/SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDTO.cs +++ b/SurveyBackend/SurveyBackend.API/DTOs/UserRegistrationDto.cs @@ -1,6 +1,6 @@ namespace SurveyBackend.DTOs; -public record UserRegistrationDTO +public record UserRegistrationDto { public string Email { get; set; } public string Username { get; set; } diff --git a/SurveyBackend/SurveyBackend.API/Program.cs b/SurveyBackend/SurveyBackend.API/Program.cs index 8aa2aca..23fd2d7 100644 --- a/SurveyBackend/SurveyBackend.API/Program.cs +++ b/SurveyBackend/SurveyBackend.API/Program.cs @@ -1,6 +1,8 @@ -using Microsoft.AspNetCore.Identity; +using System.Text; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; -using SurveyBackend.Core.Models; +using Microsoft.IdentityModel.Tokens; +using SurveyBackend.Infrastructure; using SurveyBackend.Infrastructure.Data; namespace SurveyBackend; @@ -11,33 +13,44 @@ public class Program { var builder = WebApplication.CreateBuilder(args); - // Add services to the container. + AuthOptions.MakeOptions(builder.Configuration, Environment.GetEnvironmentVariable("JWT_SECRET_KEY")); + builder.Services.AddAuthorization(); - builder.Services.AddDbContext(options => + builder.Services.AddDbContext(options => { options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")); }); - builder.Services.AddIdentity>(options => { }) - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); + builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = AuthOptions.Issuer, + ValidAudience = AuthOptions.Audience, + IssuerSigningKey = AuthOptions.SymmetricSecurityKey + }; + }); builder.Services.AddControllers(); - // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); - // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } + app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); diff --git a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj index e11596b..3764109 100644 --- a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj +++ b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj @@ -8,6 +8,7 @@ + diff --git a/SurveyBackend/SurveyBackend.API/appsettings.Development.json b/SurveyBackend/SurveyBackend.API/appsettings.Development.json index c14bf00..bcc29ea 100644 --- a/SurveyBackend/SurveyBackend.API/appsettings.Development.json +++ b/SurveyBackend/SurveyBackend.API/appsettings.Development.json @@ -7,5 +7,11 @@ }, "ConnectionStrings": { "DefaultConnection": "Data Source=Application.db" + }, + "JwtSettings": { + "SecretKey": "sigma_super_secret_key_for_jwt_tokens_yo", + "Issuer": "SurveyBackend", + "Audience": "SurveyClient", + "ExpiresInMinutes": 600 } } diff --git a/SurveyBackend/SurveyBackend.API/appsettings.json b/SurveyBackend/SurveyBackend.API/appsettings.json index 19a74e6..3b14cf8 100644 --- a/SurveyBackend/SurveyBackend.API/appsettings.json +++ b/SurveyBackend/SurveyBackend.API/appsettings.json @@ -8,5 +8,11 @@ "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Data Source=Application.db" + }, + "JwtSettings": { + "SecretKey": "sigma_super_secret_key_for_jwt_tokens_yo_that_should_be_stored_in_ENV", + "Issuer": "SurveyBackend", + "Audience": "SurveyClient", + "ExpiresInMinutes": 600 } } diff --git a/SurveyBackend/SurveyBackend.Core/Models/User.cs b/SurveyBackend/SurveyBackend.Core/Models/User.cs index ab4dcbe..78f2286 100644 --- a/SurveyBackend/SurveyBackend.Core/Models/User.cs +++ b/SurveyBackend/SurveyBackend.Core/Models/User.cs @@ -2,10 +2,14 @@ using Microsoft.AspNetCore.Identity; namespace SurveyBackend.Core.Models; -public class User : IdentityUser +public class User { + public string Id { get; set; } + public string Email { get; set; } public string FirstName { get; set; } public string LastName { get; set; } + public string Password { get; set; } + public ICollection Groups { get; set; } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs new file mode 100644 index 0000000..7d97a29 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs @@ -0,0 +1,6 @@ +namespace SurveyBackend.Core.Services; + +public interface IUserService +{ + +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj b/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj index 7bc422a..adbb79f 100644 --- a/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj +++ b/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj @@ -7,7 +7,6 @@ - diff --git a/SurveyBackend/SurveyBackend.Infrastructure/AuthOptions.cs b/SurveyBackend/SurveyBackend.Infrastructure/AuthOptions.cs new file mode 100644 index 0000000..cd379e8 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/AuthOptions.cs @@ -0,0 +1,32 @@ +using System.Text; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; + +namespace SurveyBackend.Infrastructure; + +public static class AuthOptions +{ + public static string Issuer; + public static string Audience; + public static TimeSpan TokenLifetime; + private static string? SecurityKey { get; set; } + + public static SymmetricSecurityKey SymmetricSecurityKey + { + get + { + ArgumentNullException.ThrowIfNull(SecurityKey); + + return new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey)); + } + } + + public static void MakeOptions(IConfigurationManager configurationManager, string? securityKey = null) + { + var jwtSettings = configurationManager.GetSection("JwtSettings"); + Issuer = jwtSettings["Issuer"] ?? "DefaultIssuer"; + Audience = jwtSettings["Audience"] ?? "DefaultAudience"; + TokenLifetime = TimeSpan.FromMinutes(int.Parse(jwtSettings["TokenLifetime"] ?? "60")); + SecurityKey = securityKey ?? jwtSettings["SecretKey"]; + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs similarity index 59% rename from SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs rename to SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs index a44573f..fb500db 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Data/DataContext.cs +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs @@ -5,12 +5,12 @@ using SurveyBackend.Core.Models; namespace SurveyBackend.Infrastructure.Data; -public class DataContext : IdentityDbContext, int> +public class ApplicationDbContext : DbContext { + public DbSet Users { get; set; } public DbSet Groups { get; set; } - public DataContext(DbContextOptions options) : base(options) + public ApplicationDbContext(DbContextOptions options) : base(options) { - Database.EnsureCreated(); } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Helpers/TokenHelper.cs b/SurveyBackend/SurveyBackend.Infrastructure/Helpers/TokenHelper.cs new file mode 100644 index 0000000..a52ae64 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Helpers/TokenHelper.cs @@ -0,0 +1,31 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Microsoft.IdentityModel.Tokens; +using SurveyBackend.Core.Models; + +namespace SurveyBackend.Infrastructure.Helpers; + +public class TokenHelper +{ + public static string GetAuthToken(User user) + { + var userId = user.Id.ToString(); + + var claims = new List + { + new(ClaimTypes.NameIdentifier, userId) + }; + + var jwt = new JwtSecurityToken( + claims: claims, + issuer: AuthOptions.Issuer, + audience: AuthOptions.Audience, + expires: DateTime.UtcNow + AuthOptions.TokenLifetime, + signingCredentials: new SigningCredentials(AuthOptions.SymmetricSecurityKey, SecurityAlgorithms.HmacSha256) + ); + + var token = new JwtSecurityTokenHandler().WriteToken(jwt); + + return token; + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs b/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs index 306872e..bd1980d 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs +++ b/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs @@ -7,9 +7,9 @@ namespace SurveyBackend.Infrastructure.Repositories; public class UserRepository : IUserRepository { - private readonly DataContext _context; + private readonly ApplicationDbContext _context; - public UserRepository(DataContext context) + public UserRepository(ApplicationDbContext context) { _context = context; } diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/IPasswordHasher.cs b/SurveyBackend/SurveyBackend.Infrastructure/Services/IPasswordHasher.cs new file mode 100644 index 0000000..52d1427 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Services/IPasswordHasher.cs @@ -0,0 +1,7 @@ +namespace SurveyBackend.Infrastructure.Services; + +public interface IPasswordHasher +{ + public string HashPassword(string password); + public bool Verify(string password, string hashedPassword); +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs b/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs new file mode 100644 index 0000000..f513cff --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs @@ -0,0 +1,61 @@ +using System.Security.Cryptography; + +namespace SurveyBackend.Infrastructure.Services; + +public class Sha256PasswordHasher : IPasswordHasher +{ + private const int RehashCount = 10000; + + private const int HashSizeBytes = 16; + + private const int SaltSizeBytes = 16; + + public string HashPassword(string password) + { + var saltBytes = RandomNumberGenerator.GetBytes(SaltSizeBytes); + + using var deriveBytes = GetDeriveBytes(password, saltBytes); + + var hashBytes = deriveBytes.GetBytes(HashSizeBytes); + + var fullHashBytes = new byte[SaltSizeBytes + HashSizeBytes]; + + Array.Copy(saltBytes, 0, fullHashBytes, 0, SaltSizeBytes); + Array.Copy(hashBytes, 0, fullHashBytes, SaltSizeBytes, HashSizeBytes); + + var hashedPassword = Convert.ToBase64String(fullHashBytes); + + return hashedPassword; + } + + public bool Verify(string password, string hashedPassword) + { + var fullHashBytes = Convert.FromBase64String(hashedPassword); + + var saltBytes = new byte[SaltSizeBytes]; + + Array.Copy(fullHashBytes, 0, saltBytes, 0, SaltSizeBytes); + + using var deriveBytes = GetDeriveBytes(password, saltBytes); + + var hashBytes = deriveBytes.GetBytes(HashSizeBytes); + + var isMatch = CompareHashes(hashBytes, fullHashBytes); + + return isMatch; + } + + private static bool CompareHashes(byte[] hashBytes, byte[] fullHashBytes) + { + for (var i = 0; i < HashSizeBytes; i++) + if (hashBytes[i] != fullHashBytes[i + SaltSizeBytes]) + return false; + + return true; + } + + private static Rfc2898DeriveBytes GetDeriveBytes(string password, byte[] saltBytes) + { + return new Rfc2898DeriveBytes(password, saltBytes, RehashCount, HashAlgorithmName.SHA256); + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj index eeefcab..ce7dbfc 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj +++ b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj @@ -8,6 +8,7 @@ +