using Azure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Razor.TagHelpers; using qtc_api.Dtos.User; using qtc_api.Services.EmailService; using System.IdentityModel.Tokens.Jwt; using System.Runtime.CompilerServices; using System.Security.Claims; using System.Text.Json; using ZstdSharp.Unsafe; namespace qtc_api.Controllers { [Route("api/auth")] [ApiController] public class AuthController : ControllerBase { private readonly IUserService _userService; private readonly IEmailService _emailService; private readonly ITokenService _tokenService; private readonly IHubContext _chatGWContext; private readonly IConfiguration _configuration; private readonly ServerConfig serverConfig; private readonly DataContext dataContext; public AuthController(IUserService userService, ITokenService tokenService, IHubContext chatGWContext, DataContext dataContext, IConfiguration configuration, IEmailService emailService) { _userService = userService; _tokenService = tokenService; _chatGWContext = chatGWContext; _configuration = configuration; _emailService = emailService; serverConfig = JsonSerializer.Deserialize(JsonDocument.Parse(System.IO.File.ReadAllText("./ServerConfig.json"))); this.dataContext = dataContext; } [HttpPost("register")] public async Task>> Register(UserDto userDto) { if(userDto != null) { var response = await _userService.AddUser(userDto); await _chatGWContext.Clients.All.SendAsync("RefreshUserLists"); if(response.Success != false && response.Data != null) { // send confirmation email (shouldn't do anything if email confirmation is disabled) var confirmationToken = _tokenService.GenerateEmailConfirmationToken(response.Data); var confirmationUrl = $"{Request.Scheme}://{Request.Host}/api/auth/verify-email?token={confirmationToken.Data}"; await _emailService.SendConfirmationEmail(response.Data.Email, response.Data.Username, confirmationUrl); return Ok(response); } else { return StatusCode(500, response.Message); } } else { return BadRequest(); } } [HttpPost("login")] public async Task>> Login(UserLoginDto request) { var dbUser = await _userService.GetUserByEmail(request.Email); if (dbUser.Data == null) { return Ok(new ServiceResponse { Message = "User not found.", Success = false }); } else if(!BCrypt.Net.BCrypt.Verify(request.Password, dbUser.Data.PasswordHash)) { return Ok(new ServiceResponse { Message = "Incorrect password.", Success = false }); } if(!dbUser.Data.IsEmailVerified && _configuration.GetValue("EmailConfig:EmailConfirmationRequired")) { return Ok(new ServiceResponse { Message = "You need to verify your email on this server. Check your inbox or spam. If you have not received an email, click 'Resend Verification Email'", Success = false }); } if (dbUser.Data.Id == serverConfig.AdminUserId && dbUser.Data.Role != "Admin") { dbUser.Data.Role = "Admin"; dataContext.SaveChanges(); } if (dbUser.Data.Status == 1) { return Ok(new ServiceResponse { Message = "User is already signed in.", Success = false }); } var token = await _tokenService.GenerateAccessTokenAndRefreshToken(dbUser.Data, true, request.RememberMe); return Ok(token); } [HttpPost("refresh")] public async Task>> RefreshLogin(string token) { var response = await _tokenService.ValidateRefreshToken(token); return Ok(response); } [HttpPost("resend-email")] public async Task>> ResendVerificationEmail(string email) { var user = await _userService.GetUserByEmail(email); if (user != null && user.Success && user.Data != null) { var confirmationToken = _tokenService.GenerateEmailConfirmationToken(user.Data); var confirmationUrl = $"{Request.Scheme}://{Request.Host}/api/auth/verify-email?token={confirmationToken.Data}"; await _emailService.SendConfirmationEmail(user.Data.Email, user.Data.Username, confirmationUrl); return Ok(new ServiceResponse { Success = true, Data = true }); } return Ok(new ServiceResponse { Success = false }); } [HttpGet("verify-email")] public async Task> VerifyEmail(string token) { try { var handler = new JwtSecurityTokenHandler() { InboundClaimTypeMap = new Dictionary() }; var jwt = handler.ReadJwtToken(token); if (jwt != null) { var email = jwt.Claims.FirstOrDefault(e => e.Type == ClaimTypes.Email); var id = jwt.Claims.FirstOrDefault(e => e.Type == ClaimTypes.NameIdentifier); if (email != null && id != null) { // get the user from id claim var user = await _userService.GetUserById(id.Value); if (user != null && user.Success && user.Data != null) { var now = DateTime.UtcNow; if(user.Data.Email == email.Value && now < jwt.ValidTo.ToUniversalTime()) { user.Data.IsEmailVerified = true; await dataContext.SaveChangesAsync(); return Ok("Email Verified! You may now login on the client."); } } else { return Ok("The User This Confirmation Link Is Associated With No Longer Exists."); } } } return Ok("Token Invalid. You may need to be sent another confirmation link."); } catch (SecurityTokenMalformedException) { return Ok("Token Invalid. You may need to be sent another confirmation link."); } } } }