using System.ComponentModel.DataAnnotations; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using API.Auth; using API.Validation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using Model; using Services; namespace API.Controllers; [ApiController] public class AuthenticationController(IUserService service, IConfiguration config) : ControllerBase { private readonly SymmetricSecurityKey _key = new(Encoding.UTF8.GetBytes(config["JWT:Key"]!)); [HttpGet("/auth/keep-alive")] [Authorize] public void KeepAlive() { } public record GenerateTokenRequest( [MaxLength(256, ErrorMessage = "Email address is too wide")] [EmailAddress] string Email, [MaxLength(256, ErrorMessage = "Password is too wide")] string Password ); private record AuthenticationResponse(String Token, long ExpirationDate); [HttpPost("/auth/token")] public async Task GenerateToken([FromBody] GenerateTokenRequest req) { var user = await service.Authorize(req.Email, req.Password); if (user == null) return BadRequest(new Dictionary { { "unauthorized", ["Invalid email or password"] } }); var (jwt, expirationDate) = GenerateJwt(user); return Ok(new AuthenticationResponse(jwt, expirationDate.ToFileTimeUtc())); } public record RegisterAccountRequest( [StringLength(256, MinimumLength = 4, ErrorMessage = "password length must be between 4 and 256")] [Name] string Username, [MaxLength(256, ErrorMessage = "email is longer than 256")] [EmailAddress] string Email, [StringLength(256, MinimumLength = 4, ErrorMessage = "password length must be between 4 and 256")] string Password); [HttpPost("/auth/register")] public async Task RegisterAccount([FromBody] RegisterAccountRequest req) { if (await service.GetUser(req.Email) != null) { return BadRequest(new Dictionary { { "email", ["The email address already exists"] } }); } var user = await service.CreateUser( req.Username, req.Email, req.Password, Constants.DefaultProfilePicture, false ); var (jwt, expirationDate) = GenerateJwt(user); return Ok(new AuthenticationResponse(jwt, expirationDate.ToFileTimeUtc())); } private (string, DateTime) GenerateJwt(User user) { var claims = new List { new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new(JwtRegisteredClaimNames.Sub, user.Email), new(JwtRegisteredClaimNames.Email, user.Email), new(IdentityData.IdUserClaimName, user.Id.ToString()), new(IdentityData.AdminUserClaimName, user.IsAdmin.ToString()) }; return Authentication.GenerateJwt(_key, claims); } }