refactor(authentication): Update authorization schemes to use "Bearer" for consistency

- Changed authorization schemes in AuthController, RolesController, and UsersController from JwtBearerDefaults.AuthenticationScheme to "Bearer" for uniformity across the application.
- Added new endpoints in UsersController to retrieve user roles and permissions by user ID, enhancing user management capabilities.
This commit is contained in:
Ho Ngoc Hai
2026-01-13 19:43:16 +07:00
parent 884520a766
commit 7550929f50
4 changed files with 158 additions and 40 deletions

View File

@@ -86,7 +86,7 @@ public class AuthController : ControllerBase
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Result of password change operation</returns>
[HttpPost("change-password")]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(
Summary = "Change password",
Description = "Changes the password for the currently authenticated user. Requires current password verification.",
@@ -125,7 +125,7 @@ public class AuthController : ControllerBase
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Result of logout operation</returns>
[HttpPost("logout")]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(
Summary = "Logout",
Description = "Logs out the current user and revokes all associated tokens.",
@@ -219,7 +219,7 @@ public class AuthController : ControllerBase
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>QR code and recovery codes</returns>
[HttpPost("2fa/enable")]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(
Summary = "Enable 2FA",
Description = "Initiates 2FA setup. Returns QR code and recovery codes. Must be verified with /2fa/verify.",
@@ -264,7 +264,7 @@ public class AuthController : ControllerBase
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Result of 2FA verification</returns>
[HttpPost("2fa/verify")]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(
Summary = "Verify 2FA code",
Description = "Verifies the TOTP code and completes 2FA setup.",
@@ -304,7 +304,7 @@ public class AuthController : ControllerBase
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Result of disabling 2FA</returns>
[HttpPost("2fa/disable")]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(
Summary = "Disable 2FA",
Description = "Disables 2FA for the current user. Requires verification with current 2FA code.",
@@ -438,7 +438,7 @@ public class AuthController : ControllerBase
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of linked providers</returns>
[HttpGet("linked-accounts")]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(
Summary = "Get linked accounts",
Description = "Returns list of external OAuth providers linked to current user's account.",

View File

@@ -17,7 +17,7 @@ namespace IamService.API.Controllers;
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/roles")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerTag("Role management endpoints - requires authentication")]
public class RolesController : ControllerBase
{

View File

@@ -2,11 +2,13 @@ using Asp.Versioning;
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using IamService.API.Application.Common;
using IamService.API.Application.Commands.Users;
using IamService.API.Application.Queries.Users;
using IamService.Domain.AggregatesModel.UserAggregate;
namespace IamService.API.Controllers;
@@ -17,19 +19,22 @@ namespace IamService.API.Controllers;
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/users")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerTag("User management endpoints - requires authentication")]
public class UsersController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<UsersController> _logger;
private readonly UserManager<ApplicationUser> _userManager;
public UsersController(
IMediator mediator,
ILogger<UsersController> logger)
ILogger<UsersController> logger,
UserManager<ApplicationUser> userManager)
{
_mediator = mediator;
_logger = logger;
_userManager = userManager;
}
/// <summary>
@@ -238,6 +243,89 @@ public class UsersController : ControllerBase
Roles = roles
}));
}
/// <summary>
/// EN: Get user roles by user ID.
/// VI: Lấy danh sách roles của user theo ID.
/// </summary>
/// <param name="id">User ID</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of user roles</returns>
[HttpGet("{id:guid}/roles")]
[SwaggerOperation(
Summary = "Get user roles",
Description = "Retrieves all roles assigned to a specific user.",
OperationId = "GetUserRoles")]
[SwaggerResponse(StatusCodes.Status200OK, "Successfully retrieved user roles", typeof(ApiResponse<UserRolesDto>))]
[SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
[SwaggerResponse(StatusCodes.Status404NotFound, "User not found")]
[ProducesResponseType(typeof(ApiResponse<UserRolesDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUserRoles(
[FromRoute, SwaggerParameter("User ID", Required = true)] Guid id,
CancellationToken cancellationToken = default)
{
var user = await _userManager.FindByIdAsync(id.ToString());
if (user == null)
{
return NotFound(ApiResponse<UserRolesDto>.Fail("USER_NOT_FOUND", $"User with ID {id} not found."));
}
var roles = await _userManager.GetRolesAsync(user);
return Ok(ApiResponse<UserRolesDto>.Ok(new UserRolesDto
{
UserId = id.ToString(),
Roles = roles
}));
}
/// <summary>
/// EN: Get user permissions by user ID.
/// VI: Lấy danh sách permissions của user theo ID.
/// </summary>
/// <param name="id">User ID</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of user permissions</returns>
[HttpGet("{id:guid}/permissions")]
[SwaggerOperation(
Summary = "Get user permissions",
Description = "Retrieves all permissions for a specific user (derived from roles).",
OperationId = "GetUserPermissions")]
[SwaggerResponse(StatusCodes.Status200OK, "Successfully retrieved user permissions", typeof(ApiResponse<UserPermissionsDto>))]
[SwaggerResponse(StatusCodes.Status401Unauthorized, "Authentication required")]
[SwaggerResponse(StatusCodes.Status404NotFound, "User not found")]
[ProducesResponseType(typeof(ApiResponse<UserPermissionsDto>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUserPermissions(
[FromRoute, SwaggerParameter("User ID", Required = true)] Guid id,
CancellationToken cancellationToken = default)
{
var user = await _userManager.FindByIdAsync(id.ToString());
if (user == null)
{
return NotFound(ApiResponse<UserPermissionsDto>.Fail("USER_NOT_FOUND", $"User with ID {id} not found."));
}
var roles = await _userManager.GetRolesAsync(user);
var claims = await _userManager.GetClaimsAsync(user);
// EN: Extract permissions from claims
// VI: Lấy permissions từ claims
var permissions = claims
.Where(c => c.Type == "permission")
.Select(c => c.Value)
.ToList();
return Ok(ApiResponse<UserPermissionsDto>.Ok(new UserPermissionsDto
{
UserId = id.ToString(),
Roles = roles,
Permissions = permissions
}));
}
}
/// <summary>
@@ -279,3 +367,47 @@ public class DeleteUserResult
/// </summary>
public string Message { get; set; } = string.Empty;
}
/// <summary>
/// EN: User roles response DTO.
/// VI: DTO response cho danh sách roles của user.
/// </summary>
public class UserRolesDto
{
/// <summary>
/// EN: User ID.
/// VI: ID của user.
/// </summary>
public string UserId { get; set; } = string.Empty;
/// <summary>
/// EN: List of role names assigned to the user.
/// VI: Danh sách tên roles được gán cho user.
/// </summary>
public IEnumerable<string> Roles { get; set; } = Enumerable.Empty<string>();
}
/// <summary>
/// EN: User permissions response DTO.
/// VI: DTO response cho danh sách permissions của user.
/// </summary>
public class UserPermissionsDto
{
/// <summary>
/// EN: User ID.
/// VI: ID của user.
/// </summary>
public string UserId { get; set; } = string.Empty;
/// <summary>
/// EN: List of role names assigned to the user.
/// VI: Danh sách tên roles được gán cho user.
/// </summary>
public IEnumerable<string> Roles { get; set; } = Enumerable.Empty<string>();
/// <summary>
/// EN: List of permission names assigned to the user.
/// VI: Danh sách tên permissions được gán cho user.
/// </summary>
public IEnumerable<string> Permissions { get; set; } = Enumerable.Empty<string>();
}

View File

@@ -113,40 +113,26 @@ public static class DependencyInjection
.AddAspNetIdentity<ApplicationUser>()
.AddDeveloperSigningCredential(); // EN: Use certificate in production / VI: Dùng certificate trong production
// EN: Add JWT Bearer authentication for API endpoints
// VI: Thêm JWT Bearer authentication cho API endpoints
// EN: Add JWT Bearer authentication for API endpoints using local IdentityServer
// VI: Thêm JWT Bearer authentication cho API endpoints sử dụng IdentityServer cục bộ
// EN: AddLocalApi() allows JWT validation without external metadata calls (for self-hosted IdentityServer)
// VI: AddLocalApi() cho phép JWT validation mà không cần gọi metadata bên ngoài (cho self-hosted IdentityServer)
services.AddAuthentication()
.AddJwtBearer(options =>
.AddLocalApi("Bearer", options =>
{
// EN: Configure to validate tokens from local IdentityServer
// VI: Cấu hình để validate tokens từ IdentityServer cục bộ
options.Authority = configuration["IdentityServer:Authority"] ?? "https://localhost:5001";
options.Audience = "iam-api";
options.RequireHttpsMetadata = false; // EN: Set to true in production / VI: Đặt true trong production
// EN: Disable metadata validation for testing/development
// VI: Tắt metadata validation cho testing/development
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateAudience = false, // EN: Disable for flexibility / VI: Tắt để linh hoạt
ValidateIssuer = false, // EN: Disable for testing / VI: Tắt cho testing
ValidateLifetime = true,
RequireSignedTokens = true,
ValidateIssuerSigningKey = true
};
// EN: Disable metadata address requirement if in testing environment
// VI: Tắt yêu cầu metadata address nếu trong testing environment
if (string.Equals(environmentName, "Testing", StringComparison.OrdinalIgnoreCase))
{
options.MetadataAddress = string.Empty;
options.TokenValidationParameters.ValidateIssuerSigningKey = false;
options.TokenValidationParameters.RequireSignedTokens = false;
// EN: ASP.NET Core 8+ requires JsonWebToken, not JwtSecurityToken
// VI: ASP.NET Core 8+ yêu cầu JsonWebToken, không phải JwtSecurityToken
options.TokenValidationParameters.SignatureValidator = (token, parameters) => new Microsoft.IdentityModel.JsonWebTokens.JsonWebToken(token);
}
options.ExpectedScope = "openid";
});
// EN: Configure authorization policies
// VI: Cấu hình authorization policies
services.AddAuthorization(options =>
{
options.AddPolicy("LocalApi", policy =>
{
policy.AddAuthenticationSchemes("Bearer");
policy.RequireAuthenticatedUser();
});
});
// EN: Register repositories
// VI: Đăng ký repositories