---
name: security
description: Security patterns for GoodGo platform. Use for authentication, authorization, data protection, input validation, rate limiting, or secrets management.
compatibility: ".NET 10+, ASP.NET Core Identity, Duende IdentityServer"
metadata:
  author: Velik Ho
  version: "2.0"
---

# 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

1. **Defense in Depth** - Multiple security layers
2. **Least Privilege** - Minimum required permissions
3. **Fail Secure** - Default to deny access
4. **Encrypt Sensitive Data** - PII, tokens must be encrypted
5. **Validate All Inputs** - Never trust user input
6. **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

```csharp
/// <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

```csharp
/// <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

```csharp
// 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

```csharp
/// <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ố

```csharp
/// <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

```csharp
/// <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

```csharp
// ❌ BAD: Manual hashing
var hash = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(password));

// ✅ GOOD: Use UserManager
await _userManager.CreateAsync(user, password);
```

### 2. Exposing User Existence

```csharp
// ❌ 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

```csharp
// ❌ 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

```csharp
// ❌ 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

```csharp
// 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-design/SKILL.md) - API patterns
- [Error Handling Patterns](../error-handling-patterns/SKILL.md) - Error handling
- [Project Rules](../project-rules/SKILL.md) - Coding standards
- [Skill Authoring](../skill-authoring/SKILL.md) - How to write skills
- [Detailed Examples](./references/REFERENCE.md) - Full code examples
