11 KiB
11 KiB
name, description, compatibility, metadata
| name | description | compatibility | metadata | ||||
|---|---|---|---|---|---|---|---|
| security | Security patterns for GoodGo platform. Use for authentication, authorization, data protection, input validation, rate limiting, or secrets management. | .NET 10+, ASP.NET Core Identity, Duende IdentityServer |
|
Security Patterns / Mẫu Bảo Mật
Security patterns for GoodGo microservices using ASP.NET Core Identity and Duende IdentityServer.
When to Use This Skill / Khi Nào Sử Dụng
Use this skill when:
- Implementing authentication (JWT, OAuth2, OIDC) / Triển khai authentication
- Setting up authorization (RBAC, policies) / Cài đặt authorization
- Protecting sensitive data / Bảo vệ dữ liệu nhạy cảm
- Validating user inputs / Xác thực input từ user
- Implementing 2FA / Triển khai xác thực 2 yếu tố
- Managing secrets / Quản lý secrets
- Auditing security events / Ghi log sự kiện bảo mật
Core Concepts / Khái Niệm Cốt Lõi
Security Principles / Nguyên Tắc Bảo Mật
- Defense in Depth - Multiple security layers
- Least Privilege - Minimum required permissions
- Fail Secure - Default to deny access
- Encrypt Sensitive Data - PII, tokens must be encrypted
- Validate All Inputs - Never trust user input
- Audit Everything - Log security events
ASP.NET Core Identity Stack
| Component | Purpose |
|---|---|
| ASP.NET Core Identity | User management, password hashing |
| Duende IdentityServer | OAuth2/OIDC provider |
| UserManager<T> | User CRUD operations |
| SignInManager<T> | Authentication operations |
| JWT Bearer | Token validation |
Key Patterns / Mẫu Chính
Authentication Controller / Controller Xác Thực
/// <summary>
/// EN: Authentication controller with OAuth2/OIDC support.
/// VI: Controller xác thực với OAuth2/OIDC.
/// </summary>
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/auth")]
[SwaggerTag("Authentication endpoints")]
public class AuthController : ControllerBase
{
private readonly IMediator _mediator;
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public AuthController(
IMediator mediator,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_mediator = mediator;
_userManager = userManager;
_signInManager = signInManager;
}
/// <summary>
/// EN: Register a new user.
/// VI: Đăng ký user mới.
/// </summary>
[HttpPost("register")]
[SwaggerOperation(Summary = "Register a new user")]
[SwaggerResponse(201, "User registered")]
[SwaggerResponse(409, "User already exists")]
public async Task<IActionResult> Register(
[FromBody] RegisterUserCommand command,
CancellationToken ct)
{
var result = await _mediator.Send(command, ct);
return CreatedAtAction(
nameof(Register),
ApiResponse<RegisterResult>.Ok(result));
}
}
JWT Authorization / Phân Quyền JWT
/// <summary>
/// EN: Protected endpoint requiring authentication.
/// VI: Endpoint được bảo vệ yêu cầu xác thực.
/// </summary>
[HttpPost("change-password")]
[Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(Summary = "Change password")]
public async Task<IActionResult> ChangePassword(
[FromBody] ChangePasswordRequest request,
CancellationToken ct)
{
// EN: Get user ID from JWT claims
// VI: Lấy user ID từ JWT claims
var userIdClaim = User.FindFirst("sub")?.Value
?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (string.IsNullOrEmpty(userIdClaim)
|| !Guid.TryParse(userIdClaim, out var userId))
{
return Unauthorized();
}
var command = new ChangePasswordCommand(
userId,
request.CurrentPassword,
request.NewPassword);
var result = await _mediator.Send(command, ct);
if (!result.Success)
return BadRequest(ApiResponse<object>.Fail("INVALID_PASSWORD", result.Message));
return Ok(ApiResponse<object>.Ok(result));
}
Role-Based Authorization / Phân Quyền Theo Role
// EN: Require specific role / VI: Yêu cầu role cụ thể
[Authorize(Roles = "Admin")]
[HttpDelete("users/{userId}")]
public async Task<IActionResult> DeleteUser(Guid userId) { ... }
// EN: Require policy / VI: Yêu cầu policy
[Authorize(Policy = "CanManageUsers")]
[HttpPost("users")]
public async Task<IActionResult> CreateUser(CreateUserRequest request) { ... }
// EN: Register policy in Program.cs / VI: Đăng ký policy trong Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CanManageUsers", policy =>
policy.RequireRole("Admin", "Manager"));
});
Password Hashing / Hash Mật Khẩu
/// <summary>
/// EN: ASP.NET Core Identity handles password hashing automatically.
/// VI: ASP.NET Core Identity tự động xử lý hash mật khẩu.
/// </summary>
public class RegisterUserCommandHandler : IRequestHandler<RegisterUserCommand, RegisterResult>
{
private readonly UserManager<ApplicationUser> _userManager;
public async Task<RegisterResult> Handle(
RegisterUserCommand request,
CancellationToken ct)
{
var user = new ApplicationUser
{
UserName = request.Email,
Email = request.Email,
FullName = request.FullName
};
// EN: Password is automatically hashed by UserManager
// VI: Password tự động được hash bởi UserManager
var result = await _userManager.CreateAsync(user, request.Password);
if (!result.Succeeded)
throw new ValidationException(result.Errors.First().Description);
return new RegisterResult(user.Id, user.Email);
}
}
Two-Factor Authentication / Xác Thực 2 Yếu Tố
/// <summary>
/// EN: Enable 2FA for current user.
/// VI: Bật 2FA cho user hiện tại.
/// </summary>
[HttpPost("2fa/enable")]
[Authorize(AuthenticationSchemes = "Bearer")]
[SwaggerOperation(Summary = "Enable 2FA")]
public async Task<IActionResult> Enable2FA(CancellationToken ct)
{
var userId = GetUserId();
var command = new Enable2FACommand(userId);
var result = await _mediator.Send(command, ct);
return Ok(ApiResponse<Enable2FAResponse>.Ok(new Enable2FAResponse
{
QrCodeBase64 = result.QrCodeBase64,
ManualEntryKey = result.ManualEntryKey,
RecoveryCodes = result.RecoveryCodes
}));
}
Input Validation / Xác Thực Input
/// <summary>
/// EN: Request model with DataAnnotations validation.
/// VI: Request model với validation DataAnnotations.
/// </summary>
public class RegisterUserCommand : IRequest<RegisterResult>
{
[Required]
[EmailAddress]
public string Email { get; set; } = string.Empty;
[Required]
[MinLength(8)]
[RegularExpression(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).+$",
ErrorMessage = "Password must have uppercase, lowercase, digit, special char")]
public string Password { get; set; } = string.Empty;
[MaxLength(100)]
public string? FullName { get; set; }
}
// EN: FluentValidation alternative / VI: Thay thế bằng FluentValidation
public class RegisterUserCommandValidator : AbstractValidator<RegisterUserCommand>
{
public RegisterUserCommandValidator()
{
RuleFor(x => x.Email)
.NotEmpty().EmailAddress();
RuleFor(x => x.Password)
.NotEmpty()
.MinimumLength(8)
.Matches("[A-Z]").WithMessage("Must have uppercase")
.Matches("[a-z]").WithMessage("Must have lowercase")
.Matches("[0-9]").WithMessage("Must have digit")
.Matches("[^a-zA-Z0-9]").WithMessage("Must have special char");
}
}
Common Mistakes / Lỗi Thường Gặp
1. Not Using Identity's Password Hasher
// ❌ BAD: Manual hashing
var hash = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(password));
// ✅ GOOD: Use UserManager
await _userManager.CreateAsync(user, password);
2. Exposing User Existence
// ❌ BAD: Reveals if user exists
if (user == null) return NotFound("User not found");
if (!valid) return BadRequest("Wrong password");
// ✅ GOOD: Generic message
if (user == null || !await _userManager.CheckPasswordAsync(user, password))
return Unauthorized(ApiResponse<object>.Fail("INVALID_CREDENTIALS", "Invalid credentials"));
3. Missing Authorization Attribute
// ❌ BAD: No authorization
[HttpDelete("users/{id}")]
public async Task<IActionResult> DeleteUser(Guid id) { ... }
// ✅ GOOD: With authorization
[Authorize(Roles = "Admin")]
[HttpDelete("users/{id}")]
public async Task<IActionResult> DeleteUser(Guid id) { ... }
4. Logging Sensitive Data
// ❌ BAD: Logging password
_logger.LogInformation("Login attempt: {Email} {Password}", email, password);
// ✅ GOOD: Redact sensitive data
_logger.LogInformation("Login attempt: {Email}", email);
Quick Reference / Tham Chiếu Nhanh
Authentication Attributes
| Attribute | Purpose |
|---|---|
[Authorize] |
Require authentication |
[Authorize(Roles = "Admin")] |
Require specific role |
[Authorize(Policy = "PolicyName")] |
Require policy |
[AllowAnonymous] |
Allow unauthenticated |
Common Status Codes
| Code | Meaning | When to Use |
|---|---|---|
| 401 | Unauthorized | Missing/invalid token |
| 403 | Forbidden | Valid token, no permission |
| 409 | Conflict | User already exists |
JWT Claims
// EN: Get user ID from claims / VI: Lấy user ID từ claims
var userId = User.FindFirst("sub")?.Value
?? User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
// EN: Check role / VI: Kiểm tra role
var isAdmin = User.IsInRole("Admin");
// EN: Get email / VI: Lấy email
var email = User.FindFirst(ClaimTypes.Email)?.Value;
Security Checklist
- All endpoints have
[Authorize]or[AllowAnonymous] - Passwords use ASP.NET Core Identity
- Sensitive data is not logged
- Error messages don't expose user existence
- 2FA is available for users
- JWT tokens have proper expiry
- Secrets are in environment variables
- Input validation with DataAnnotations/FluentValidation
Resources / Tài Nguyên
- API Design - API patterns
- Error Handling Patterns - Error handling
- Project Rules - Coding standards
- Skill Authoring - How to write skills
- Detailed Examples - Full code examples