c4a1775d7d
- Add per-account lockout + IP rate limiter on local sign-in (A07) - Emit CSP and security headers on every response (A05) - Run container as non-root `app`, /data 0700 (A05/A02) - Stop reflecting raw token-endpoint body into redirect URL (A09) - Handle missing refresh_token in connect callback without a 500 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
32 lines
1.6 KiB
C#
32 lines
1.6 KiB
C#
namespace SharepointToolbox.Web.Core.Models;
|
|
|
|
public class AppUser
|
|
{
|
|
public string Id { get; set; } = Guid.NewGuid().ToString();
|
|
public string Email { get; set; } = string.Empty;
|
|
public string DisplayName { get; set; } = string.Empty;
|
|
public UserRole Role { get; set; } = UserRole.TechN0;
|
|
|
|
/// <summary>Identity source. Entra = OIDC-provisioned, Local = password-based account.</summary>
|
|
public AuthProvider Provider { get; set; } = AuthProvider.Entra;
|
|
|
|
/// <summary>PasswordHasher output. Only set for <see cref="AuthProvider.Local"/> users.</summary>
|
|
public string? PasswordHash { get; set; }
|
|
|
|
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
|
|
public DateTimeOffset? LastLogin { get; set; }
|
|
|
|
// ── Local-account brute-force lockout ───────────────────────────────────────
|
|
// Consecutive failed password attempts and, once the threshold is hit, the UTC
|
|
// instant the account unlocks again. Only meaningful for AuthProvider.Local.
|
|
// A per-account counter (not just an IP rate limiter) is the control that holds
|
|
// up here: forwarded headers are trusted from any source, so an attacker who can
|
|
// rotate X-Forwarded-For would evade IP-based throttling but not this.
|
|
|
|
/// <summary>Consecutive failed local-login attempts since the last success.</summary>
|
|
public int FailedLoginCount { get; set; }
|
|
|
|
/// <summary>UTC instant the account unlocks; null when not locked.</summary>
|
|
public DateTimeOffset? LockoutEndUtc { get; set; }
|
|
}
|