Add versioned legal consent to authentication
Deploy Promiscuity Auth API / deploy (push) Successful in 1m29s
k8s smoke test / test (push) Successful in 20s

This commit is contained in:
2026-07-20 02:16:02 -05:00
parent 4121c59484
commit 94ffe6e7b7
9 changed files with 189 additions and 9 deletions
@@ -21,13 +21,18 @@ public class AuthController : ControllerBase
private readonly BlacklistService _blacklist;
private readonly IHttpClientFactory _httpClients;
private readonly JwtTokenService _tokens;
private readonly LegalPolicy _legal;
public AuthController(UserService users, IConfiguration cfg, BlacklistService blacklist,
IHttpClientFactory httpClients, JwtTokenService tokens)
IHttpClientFactory httpClients, JwtTokenService tokens, LegalPolicy legal)
{
_users = users; _cfg = cfg; _blacklist = blacklist; _httpClients = httpClients; _tokens = tokens;
_users = users; _cfg = cfg; _blacklist = blacklist; _httpClients = httpClients; _tokens = tokens; _legal = legal;
}
[HttpGet("legal")]
[AllowAnonymous]
public IActionResult Legal() => Ok(_legal.Status());
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegisterRequest req)
{
@@ -35,6 +40,9 @@ public class AuthController : ControllerBase
var password = req.Password ?? "";
var email = req.Email?.Trim() ?? "";
if (!req.AcceptLegal)
return BadRequest("You must agree to the Terms of Service and acknowledge the Privacy Policy");
if (!Regex.IsMatch(username, "^[A-Za-z0-9_]{3,24}$"))
return BadRequest("Username must be 3-24 characters using only letters, numbers, or underscores");
if (password.Length < 8)
@@ -48,6 +56,7 @@ public class AuthController : ControllerBase
var hash = BCrypt.Net.BCrypt.HashPassword(password);
var user = new User { Username = username, PasswordHash = hash, Role = "USER", Email = email, CreatedUtc = DateTime.UtcNow };
_legal.Accept(user, "password_registration");
try
{
await _users.CreateAsync(user);
@@ -72,7 +81,7 @@ public class AuthController : ControllerBase
user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(7);
await _users.UpdateAsync(user);
return Ok(new { accessToken, refreshToken = user.RefreshToken, user.Username, user.Role, jti, exp = expUtc });
return Ok(SessionResponse(user, accessToken, expUtc, jti, user.RefreshToken));
}
[HttpPost("refresh")]
@@ -83,7 +92,7 @@ public class AuthController : ControllerBase
return Unauthorized("Invalid or expired refresh token");
var (accessToken, _, expUtc) = _tokens.Generate(user);
return Ok(new { accessToken, exp = expUtc });
return Ok(SessionResponse(user, accessToken, expUtc));
}
[HttpPost("logout")]
@@ -105,6 +114,18 @@ public class AuthController : ControllerBase
return user is null ? Unauthorized() : Ok(ProfileResponse(user));
}
[HttpPost("legal/accept")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> AcceptLegal([FromBody] AcceptLegalRequest req)
{
if (!req.Accept) return BadRequest("Legal acceptance is required.");
var user = await CurrentUserAsync();
if (user is null) return Unauthorized();
_legal.Accept(user, "existing_user_prompt");
await _users.UpdateAsync(user);
return Ok(_legal.Status(user));
}
[HttpPatch("me")]
[Authorize(Roles = "USER,SUPER")]
public async Task<IActionResult> UpdateProfile([FromBody] UpdateProfileRequest req)
@@ -204,14 +225,27 @@ public class AuthController : ControllerBase
return ObjectId.TryParse(user.Id, out var id) ? id.CreationTime : DateTime.UtcNow;
}
private static object ProfileResponse(User user) => new
private object ProfileResponse(User user) => new
{
userId = user.Id,
user.Username,
user.DisplayName,
user.Email,
user.Role,
createdUtc = user.CreatedUtc ?? CreationDate(user)
createdUtc = user.CreatedUtc ?? CreationDate(user),
legal = _legal.Status(user)
};
private object SessionResponse(User user, string accessToken, DateTime expUtc,
string? jti = null, string? refreshToken = null) => new
{
accessToken,
refreshToken,
user.Username,
user.Role,
jti,
exp = expUtc,
legal = _legal.Status(user)
};
private async Task RevokeCurrentTokenAsync()
@@ -19,10 +19,11 @@ public class SteamAuthController : ControllerBase
private readonly JwtTokenService _tokens;
private readonly IConfiguration _cfg;
private readonly ILogger<SteamAuthController> _logger;
private readonly LegalPolicy _legal;
public SteamAuthController(UserService users, SteamAuthTransactionService transactions,
SteamOpenIdService openId, JwtTokenService tokens, IConfiguration cfg,
ILogger<SteamAuthController> logger)
ILogger<SteamAuthController> logger, LegalPolicy legal)
{
_users = users;
_transactions = transactions;
@@ -30,6 +31,7 @@ public class SteamAuthController : ControllerBase
_tokens = tokens;
_cfg = cfg;
_logger = logger;
_legal = legal;
}
[HttpPost("start")]
@@ -115,7 +117,8 @@ public class SteamAuthController : ControllerBase
user.Username,
user.Role,
jti,
exp
exp,
legal = _legal.Status(user)
});
}
@@ -133,6 +136,8 @@ public class SteamAuthController : ControllerBase
var createdUser = false;
if (request.Action == "create")
{
if (!request.AcceptLegal)
return BadRequest(new { message = "You must agree to the Terms of Service and acknowledge the Privacy Policy." });
var validation = ValidateNewAccount(request);
if (validation is not null)
return BadRequest(new { message = validation });
@@ -149,6 +154,7 @@ public class SteamAuthController : ControllerBase
RefreshToken = NewRefreshToken(),
RefreshTokenExpiry = DateTime.UtcNow.AddDays(7)
};
_legal.Accept(user, "steam_registration");
try
{
await _users.CreateAsync(user);