diff --git a/api/src/Controllers/AuthController.cs b/api/src/Controllers/AuthController.cs new file mode 100644 index 0000000..0b53759 --- /dev/null +++ b/api/src/Controllers/AuthController.cs @@ -0,0 +1,62 @@ + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; + +using agologumApi.Models; +using agologumApi.Services; + +[ApiController] +[Route("api/[controller]")] +public class AuthController : ControllerBase { + + private readonly UserService users_; + private readonly JwtService jwt_; + + public AuthController(UserService users, JwtService jwt) + { + users_ = users; + jwt_ = jwt; + } + + [HttpPost("register")] + public async Task Register(RegisterDto dto) { + var user = new User { + Name = dto.Username, + PasswordHash = BCrypt.Net.BCrypt.HashPassword(dto.Password) // TODO: hashing stage in client + }; + + var newUser = await users_.Create(user); + return CreatedAtAction( + nameof(Register), + new { id = newUser.Id }, + user + ); + } + + [HttpPost("login")] + public async Task Login(LoginDto dto) + { + var user = await users_.Get(dto.Username); + + if (user == null || !BCrypt.Net.BCrypt.Verify(dto.Password, user.PasswordHash)) { + return Unauthorized(); + } + + var token = jwt_.GenerateJwt(user); + + return Ok(new { token }); + } + + [Authorize] // authorize is handled by middleware + [HttpPost("logout")] + public ActionResult Logout() { + // dummy endpoint + // logout happens upon client-side jwt removal + return Ok(); + } + + // TODO + // refresh tokens + // email verification + // password reset +} \ No newline at end of file diff --git a/api/src/Models/DTO.cs b/api/src/Models/DTO.cs new file mode 100644 index 0000000..ea508f6 --- /dev/null +++ b/api/src/Models/DTO.cs @@ -0,0 +1,15 @@ + +public class RegisterDto { + + public string Username { get; set; } = ""; + public string Email { get; set; } = ""; + public string Password { get; set; } = ""; + +} + +public class LoginDto { + + public string Username { get; set; } = ""; + public string Password { get; set; } = ""; + +} diff --git a/api/src/Services/JwtService.cs b/api/src/Services/JwtService.cs new file mode 100644 index 0000000..14e0b69 --- /dev/null +++ b/api/src/Services/JwtService.cs @@ -0,0 +1,42 @@ + +using Microsoft.IdentityModel.Tokens; +using System.Text; +using System.Security.Claims; +using System.IdentityModel.Tokens.Jwt; + +using agologumApi.Models; + +public class JwtService { + + private readonly IConfiguration config_; + + public JwtService(IConfiguration config) { // why the heck does c# not have initializer lists ? + config_ = config; + } + + public string? GenerateJwt(User user) { + + string? jwtKey = config_["Jwt:Key"]; + if(jwtKey == null) return null; + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)); + + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + // not too sure + var claims = new[] { + new Claim(ClaimTypes.Name, user.Name), + new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()) + }; + + var token = new JwtSecurityToken( + claims: claims, + expires: DateTime.UtcNow.AddHours(2), // will add a refresher later + signingCredentials: creds + ); + + return new JwtSecurityTokenHandler().WriteToken(token); + + } + + +} \ No newline at end of file