using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.AspNetCore.Identity; using Microsoft.IdentityModel.Tokens; namespace WhatApi.Endpoints; public class LoginEndpoint(AppDatabase db, IConfiguration configuration) : BaseEndpoint { public class LoginRequest { public required string Username { get; set; } public required string Password { get; set; } } [AllowAnonymous] [HttpPost("~/login")] public async Task HandleAsync([FromForm] LoginRequest login, CancellationToken ct = default) { var user = await db.Users.FirstOrDefaultAsync(c => c.Name == login.Username, ct); if (user?.PasswordHash is null) return Unauthorized(); var verificationResult = PasswordHasher.VerifyHashedPassword(user.PasswordHash, login.Password); if (verificationResult == PasswordVerificationResult.Failed) return Unauthorized(); var tokenEntropy = configuration.GetValue(Constants.Env.TokenEntropy); ArgumentException.ThrowIfNullOrWhiteSpace(tokenEntropy); var key = Encoding.ASCII.GetBytes(tokenEntropy); var tokenIssuer = configuration.GetValue(Constants.Env.TokenIssuer); var tokenAudience = configuration.GetValue(Constants.Env.TokenAudience); var tokenHandler = new JwtSecurityTokenHandler(); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity([ new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Name) ]), Expires = DateTime.UtcNow.AddMinutes(60), Issuer = tokenIssuer, Audience = tokenAudience, SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); user.SetLastSeen(); await db.SaveChangesAsync(ct); return Redirect("what://lcb?code=" + tokenString); } }