From ef72cbea18bb50cb79fdf4e6cec063976f06798b Mon Sep 17 00:00:00 2001 From: Lasse Rune Hansen Date: Sun, 14 Jun 2026 17:38:27 +0200 Subject: [PATCH] fix(backend/auth): Fix JWT token claim mapping for user ID - Changed AuthService.GenerateJwtToken to use JWT standard claims: - JwtRegisteredClaimNames.Sub for user ID (instead of ClaimTypes.NameIdentifier) - JwtRegisteredClaimNames.Name for username - JwtRegisteredClaimNames.Email for email - JwtRegisteredClaimNames.UniqueName for additional username claim - Kept ClaimTypes.Role for role - Updated AuthController.GetCurrentUser to use JwtRegisteredClaimNames.Sub - Updated AdminController.DeleteUserAsync to use JwtRegisteredClaimNames.Sub This fixes the 401 error when calling /me after login. The issue was that tokens were being generated with ClaimTypes.NameIdentifier claim, but the JWT middleware doesn't automatically map this to a claim that can be found with User.FindFirst(). Using standard JWT claims (sub, name, email) ensures proper compatibility with ASP.NET Core's JWT authentication. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- GermanApp/Infrastructure/Services/AuthService.cs | 9 ++++++--- GermanApp/Presentation/Controllers/AdminController.cs | 8 +++++++- GermanApp/Presentation/Controllers/AuthController.cs | 7 +++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/GermanApp/Infrastructure/Services/AuthService.cs b/GermanApp/Infrastructure/Services/AuthService.cs index 58fa997..53a1f3e 100644 --- a/GermanApp/Infrastructure/Services/AuthService.cs +++ b/GermanApp/Infrastructure/Services/AuthService.cs @@ -151,11 +151,14 @@ public class AuthService : IAuthService var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); + // Use JWT standard claims for better compatibility + // "sub" = subject (user identifier), "name" = username, "email" = email, "role" = user role var claims = new[] { - new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), - new Claim(ClaimTypes.Name, user.Username), - new Claim(ClaimTypes.Email, user.Email), + new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), // JWT standard: sub for subject/user ID + new Claim(JwtRegisteredClaimNames.Name, user.Username), + new Claim(JwtRegisteredClaimNames.Email, user.Email), + new Claim(JwtRegisteredClaimNames.UniqueName, user.Username), // Additional: unique name new Claim(ClaimTypes.Role, user.Role) // Use user's actual role (User or Admin) }; diff --git a/GermanApp/Presentation/Controllers/AdminController.cs b/GermanApp/Presentation/Controllers/AdminController.cs index 5d96d7f..1f25f66 100644 --- a/GermanApp/Presentation/Controllers/AdminController.cs +++ b/GermanApp/Presentation/Controllers/AdminController.cs @@ -4,7 +4,9 @@ using GermanApp.Application.DTOs.Auth; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; using System.Net; +using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -114,7 +116,11 @@ public class AdminController : ControllerBase { try { - var adminUserId = int.Parse(User.FindFirst("nameid")?.Value ?? "0"); + // Get admin user ID from JWT "sub" claim (standard JWT claim for subject/user ID) + var adminUserIdClaim = User.FindFirst(JwtRegisteredClaimNames.Sub) ?? User.FindFirst("sub"); + if (adminUserIdClaim == null || !int.TryParse(adminUserIdClaim.Value, out var adminUserId) || adminUserId == 0) + return Unauthorized(); + var result = await _adminService.DeleteUserAsync(userId, adminUserId, cancellationToken); if (!result) diff --git a/GermanApp/Presentation/Controllers/AuthController.cs b/GermanApp/Presentation/Controllers/AuthController.cs index 265cbf2..c8c8a3f 100644 --- a/GermanApp/Presentation/Controllers/AuthController.cs +++ b/GermanApp/Presentation/Controllers/AuthController.cs @@ -2,7 +2,9 @@ using GermanApp.Application.DTOs.Auth; using GermanApp.Application.Interfaces; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using System.IdentityModel.Tokens.Jwt; using System.Net; +using System.Security.Claims; namespace GermanApp.Presentation.Controllers; @@ -85,8 +87,9 @@ public class AuthController : ControllerBase { try { - var userId = int.Parse(User.FindFirst("nameid")?.Value ?? "0"); - if (userId == 0) + // Get user ID from JWT "sub" claim (standard JWT claim for subject/user ID) + var userIdClaim = User.FindFirst(JwtRegisteredClaimNames.Sub) ?? User.FindFirst("sub"); + if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId) || userId == 0) return Unauthorized(); var user = await _authService.GetCurrentUserAsync(userId);