From d3a5a5edee3e4c4bb9404e7b6601fdb97020b942 Mon Sep 17 00:00:00 2001 From: jarvis Date: Sun, 25 Jan 2026 15:03:28 +0000 Subject: [PATCH 1/2] fix: Add missing authorization to Identity endpoints - ChangePasswordEndpoint: Add RequireAuthorization() for logged-in users - GetUserProfileEndpoint: Add RequireAuthorization() for logged-in users - AssignUserRolesEndpoint: Add RequirePermission(Users.ManageRoles) - GetUserPermissionsEndpoint: Add RequirePermission(Users.View) - Add Users.ManageRoles permission constant These endpoints were previously accessible without proper authorization checks. --- .../Shared/Identity/IdentityPermissionConstants.cs | 1 + .../v1/Users/AssignUserRoles/AssignUserRolesEndpoint.cs | 7 +++++-- .../v1/Users/ChangePassword/ChangePasswordEndpoint.cs | 3 ++- .../Users/GetUserPermissions/GetUserPermissionsEndpoint.cs | 5 ++++- .../v1/Users/GetUserProfile/GetUserProfileEndpoint.cs | 3 ++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/BuildingBlocks/Shared/Identity/IdentityPermissionConstants.cs b/src/BuildingBlocks/Shared/Identity/IdentityPermissionConstants.cs index 67aca149c4..860a11e56d 100644 --- a/src/BuildingBlocks/Shared/Identity/IdentityPermissionConstants.cs +++ b/src/BuildingBlocks/Shared/Identity/IdentityPermissionConstants.cs @@ -8,6 +8,7 @@ public static class Users public const string Create = "Permissions.Users.Create"; public const string Update = "Permissions.Users.Update"; public const string Delete = "Permissions.Users.Delete"; + public const string ManageRoles = "Permissions.Users.ManageRoles"; } public static class Roles diff --git a/src/Modules/Identity/Modules.Identity/Features/v1/Users/AssignUserRoles/AssignUserRolesEndpoint.cs b/src/Modules/Identity/Modules.Identity/Features/v1/Users/AssignUserRoles/AssignUserRolesEndpoint.cs index 9deb92fb79..fac44d76be 100644 --- a/src/Modules/Identity/Modules.Identity/Features/v1/Users/AssignUserRoles/AssignUserRolesEndpoint.cs +++ b/src/Modules/Identity/Modules.Identity/Features/v1/Users/AssignUserRoles/AssignUserRolesEndpoint.cs @@ -1,4 +1,6 @@ -using FSH.Modules.Identity.Contracts.v1.Users.AssignUserRoles; +using FSH.Framework.Shared.Identity; +using FSH.Framework.Shared.Identity.Authorization; +using FSH.Modules.Identity.Contracts.v1.Users.AssignUserRoles; using Mediator; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -27,6 +29,7 @@ internal static RouteHandlerBuilder MapAssignUserRolesEndpoint(this IEndpointRou }) .WithName("AssignUserRoles") .WithSummary("Assign roles to user") - .WithDescription("Assign one or more roles to a user."); + .WithDescription("Assign one or more roles to a user.") + .RequirePermission(IdentityPermissionConstants.Users.ManageRoles); } } diff --git a/src/Modules/Identity/Modules.Identity/Features/v1/Users/ChangePassword/ChangePasswordEndpoint.cs b/src/Modules/Identity/Modules.Identity/Features/v1/Users/ChangePassword/ChangePasswordEndpoint.cs index 12962e6cdd..57b84d04a8 100644 --- a/src/Modules/Identity/Modules.Identity/Features/v1/Users/ChangePassword/ChangePasswordEndpoint.cs +++ b/src/Modules/Identity/Modules.Identity/Features/v1/Users/ChangePassword/ChangePasswordEndpoint.cs @@ -21,6 +21,7 @@ internal static RouteHandlerBuilder MapChangePasswordEndpoint(this IEndpointRout }) .WithName("ChangePassword") .WithSummary("Change password") - .WithDescription("Change the current user's password."); + .WithDescription("Change the current user's password.") + .RequireAuthorization(); } } diff --git a/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserPermissions/GetUserPermissionsEndpoint.cs b/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserPermissions/GetUserPermissionsEndpoint.cs index 627d3d82b7..e3affb8e09 100644 --- a/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserPermissions/GetUserPermissionsEndpoint.cs +++ b/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserPermissions/GetUserPermissionsEndpoint.cs @@ -1,5 +1,7 @@ using System.Security.Claims; using FSH.Framework.Core.Exceptions; +using FSH.Framework.Shared.Identity; +using FSH.Framework.Shared.Identity.Authorization; using FSH.Framework.Shared.Identity.Claims; using FSH.Modules.Identity.Contracts.v1.Users.GetUserPermissions; using Mediator; @@ -24,6 +26,7 @@ internal static RouteHandlerBuilder MapGetCurrentUserPermissionsEndpoint(this IE }) .WithName("GetCurrentUserPermissions") .WithSummary("Get current user permissions") - .WithDescription("Retrieve permissions for the authenticated user."); + .WithDescription("Retrieve permissions for the authenticated user.") + .RequirePermission(IdentityPermissionConstants.Users.View); } } diff --git a/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs b/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs index b64e88b7a3..e5bc00fa77 100644 --- a/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs +++ b/src/Modules/Identity/Modules.Identity/Features/v1/Users/GetUserProfile/GetUserProfileEndpoint.cs @@ -24,6 +24,7 @@ internal static RouteHandlerBuilder MapGetMeEndpoint(this IEndpointRouteBuilder }) .WithName("GetCurrentUserProfile") .WithSummary("Get current user profile") - .WithDescription("Retrieve the authenticated user's profile from the access token."); + .WithDescription("Retrieve the authenticated user's profile from the access token.") + .RequireAuthorization(); } } From d65c8e7cf863547b05d99fd3e67251b735b28d5d Mon Sep 17 00:00:00 2001 From: jarvis Date: Sun, 25 Jan 2026 15:03:49 +0000 Subject: [PATCH 2/2] fix: Add CancellationToken to Identity handlers - PathAwareAuthorizationHandler: Pass context.RequestAborted to WriteAsync - RequiredPermissionAuthorizationHandler: Extract CancellationToken from HttpContext and pass to HasPermissionAsync Note: UserRegisteredEmailHandler and TokenGeneratedLogHandler already have proper CancellationToken handling. --- .../Authorization/PathAwareAuthorizationHandler.cs | 4 ++-- .../RequiredPermissionAuthorizationHandler.cs | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Modules/Identity/Modules.Identity/Authorization/PathAwareAuthorizationHandler.cs b/src/Modules/Identity/Modules.Identity/Authorization/PathAwareAuthorizationHandler.cs index b06e8e1fe2..2299c44c6a 100644 --- a/src/Modules/Identity/Modules.Identity/Authorization/PathAwareAuthorizationHandler.cs +++ b/src/Modules/Identity/Modules.Identity/Authorization/PathAwareAuthorizationHandler.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization.Policy; using Microsoft.AspNetCore.Http; @@ -38,7 +38,7 @@ public async Task HandleAsync( // If no endpoint is found, return 404 explicitly context.Response.StatusCode = StatusCodes.Status404NotFound; - await context.Response.WriteAsync("Endpoint not found."); + await context.Response.WriteAsync("Endpoint not found.", context.RequestAborted).ConfigureAwait(false); return; } diff --git a/src/Modules/Identity/Modules.Identity/Authorization/RequiredPermissionAuthorizationHandler.cs b/src/Modules/Identity/Modules.Identity/Authorization/RequiredPermissionAuthorizationHandler.cs index ae9a13d9af..e9f7a0a908 100644 --- a/src/Modules/Identity/Modules.Identity/Authorization/RequiredPermissionAuthorizationHandler.cs +++ b/src/Modules/Identity/Modules.Identity/Authorization/RequiredPermissionAuthorizationHandler.cs @@ -1,4 +1,4 @@ -using FSH.Framework.Shared.Identity.Claims; +using FSH.Framework.Shared.Identity.Claims; using FSH.Modules.Identity.Contracts.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -12,9 +12,10 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(requirement); + var httpContext = context.Resource as HttpContext; var endpoint = context.Resource switch { - HttpContext httpContext => httpContext.GetEndpoint(), + HttpContext ctx => ctx.GetEndpoint(), Endpoint ep => ep, _ => null, }; @@ -27,7 +28,9 @@ protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context.Succeed(requirement); return; } - if (context.User?.GetUserId() is { } userId && await userService.HasPermissionAsync(userId, requiredPermissions.First())) + + var cancellationToken = httpContext?.RequestAborted ?? CancellationToken.None; + if (context.User?.GetUserId() is { } userId && await userService.HasPermissionAsync(userId, requiredPermissions.First(), cancellationToken).ConfigureAwait(false)) { context.Succeed(requirement); }