From ce30b8debff88720f9fa01d5af1fcad3c0bd7a57 Mon Sep 17 00:00:00 2001 From: VPKSoft Date: Wed, 19 Mar 2025 11:50:25 +0200 Subject: [PATCH] Add logged in user detection. --- PasswordKeeper.BusinessLogic/Users.cs | 2 +- PasswordKeeper.DataAccess/Users.cs | 6 ++--- .../Controllers/AuthenticationController.cs | 27 ++++++++++++++++--- PasswordKeeperServer/Program.cs | 7 ++++- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/PasswordKeeper.BusinessLogic/Users.cs b/PasswordKeeper.BusinessLogic/Users.cs index 8e30c14..dabfda5 100644 --- a/PasswordKeeper.BusinessLogic/Users.cs +++ b/PasswordKeeper.BusinessLogic/Users.cs @@ -10,7 +10,7 @@ namespace PasswordKeeper.BusinessLogic; public class Users(PasswordKeeper.DataAccess.Users users) { /// - public async Task UpsertUser(UserDto userDto) + public async Task UpsertUser(UserDto userDto) { return await users.UpsertUser(userDto); } diff --git a/PasswordKeeper.DataAccess/Users.cs b/PasswordKeeper.DataAccess/Users.cs index a1de3cb..17c749b 100644 --- a/PasswordKeeper.DataAccess/Users.cs +++ b/PasswordKeeper.DataAccess/Users.cs @@ -30,8 +30,8 @@ public class Users(IDbContextFactory dbContextFactory, IMapper mapper) /// Upserts a user. If the user doesn't exist, inserts it, otherwise updates it. /// /// The user to upsert. - /// true if the user was upserted successfully, otherwise false. - public async Task UpsertUser(UserDto userDto) + /// The upserted user data or null if the operation failed. + public async Task UpsertUser(UserDto userDto) { await using var context = await dbContextFactory.CreateDbContextAsync(); @@ -49,6 +49,6 @@ public async Task UpsertUser(UserDto userDto) await context.SaveChangesAsync(); - return true; + return mapper.Map(user); } } \ No newline at end of file diff --git a/PasswordKeeperServer/Controllers/AuthenticationController.cs b/PasswordKeeperServer/Controllers/AuthenticationController.cs index fbbe293..e2b9bca 100644 --- a/PasswordKeeperServer/Controllers/AuthenticationController.cs +++ b/PasswordKeeperServer/Controllers/AuthenticationController.cs @@ -1,4 +1,5 @@ using System.IdentityModel.Tokens.Jwt; +using System.Security.Authentication; using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -47,9 +48,9 @@ public async Task Login([FromBody] UserLogin user) PasswordSalt = Convert.ToBase64String(salt!), }; - await users.UpsertUser(userDto); + userDto = await users.UpsertUser(userDto); - var token = GenerateJwtToken(user.Username); + var token = GenerateJwtToken(user.Username, userDto.Id); return Ok(new { token, }); } @@ -59,7 +60,7 @@ public async Task Login([FromBody] UserLogin user) if (Users.VerifyPassword(user.Password, userDto.PasswordHash, Convert.FromBase64String(userDto.PasswordSalt))) { - var token = GenerateJwtToken(user.Username); + var token = GenerateJwtToken(user.Username, userDto.Id); return Ok(new { token, }); } } @@ -77,17 +78,35 @@ public IActionResult TestUnauthorized() return Ok(); } + /// + /// Gets the logged-in user's ID. + /// + /// The logged-in user's ID, or -1 if the claim containing the user ID is not found. + long GetLoggedUserId() + { + var claim = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier && long.TryParse(c.Value, out _)); + + if (long.TryParse(claim?.Value, out var result)) + { + return result; + } + + return -1; + } + /// /// Generates a JWT token for the given username. /// /// The username to generate the JWT token for. + /// The user ID to generate the JWT token for. /// The JWT token. - private string GenerateJwtToken(string username) + private string GenerateJwtToken(string username, long userId) { var claims = new[] { new Claim(JwtRegisteredClaimNames.Sub, username), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim(JwtRegisteredClaimNames.NameId, userId.ToString()), }; var key = new SymmetricSecurityKey(Program.JwtKey); diff --git a/PasswordKeeperServer/Program.cs b/PasswordKeeperServer/Program.cs index 388ca05..0bebffe 100644 --- a/PasswordKeeperServer/Program.cs +++ b/PasswordKeeperServer/Program.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi; using MySql.Data.MySqlClient; using PasswordKeeper.BusinessLogic; using PasswordKeeper.DAO; @@ -70,7 +71,11 @@ public static void Main(string[] args) builder.Services.AddAutoMapper(typeof(AutoMapperProfile)); // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi - builder.Services.AddOpenApi(); + builder.Services.AddOpenApi("v3",options => + { + options.ShouldInclude = operation => operation.HttpMethod != null; + options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; + }); var app = builder.Build();