using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using RdtClient.Data.Enums; using RdtClient.Service.Services; using System.ComponentModel.DataAnnotations; namespace RdtClient.Web.Controllers; /// /// Handles authentication and user management operations /// [Route("Api/Authentication")] public class AuthController(Authentication authentication, Settings settings) : Controller { /// /// Checks if the current session is authenticated /// /// Session is valid and authenticated, or authentication is disabled /// System requires initial setup - no users exist in the database /// Session is not authenticated [AllowAnonymous] [Route("IsLoggedIn")] [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status402PaymentRequired, Type = typeof(ProblemDetails))] [ProducesResponseType(StatusCodes.Status403Forbidden, Type = typeof(ProblemDetails))] public async Task IsLoggedIn() { if (Settings.Get.General.AuthenticationType == AuthenticationType.None) { return Ok(); } if (User.Identity?.IsAuthenticated == false) { var user = await authentication.GetUser(); if (user == null) { return Problem(detail: "Setup required", statusCode: StatusCodes.Status402PaymentRequired); } return Problem(detail: "Login required", statusCode: StatusCodes.Status403Forbidden); } return Ok(); } /// /// Creates the initial account for the system /// /// /// This endpoint can only be used when no account exists. /// It creates the account and automatically logs in. /// /// Registration details for the new account /// Account created successfully and user is logged in /// Invalid request - username/password missing or validation failed /// Cannot create account - a user already exists in system [AllowAnonymous] [Route("Create")] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))] [ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(ProblemDetails))] public async Task Create([FromBody] AuthControllerLoginRequest? request) { if (request == null) { return BadRequest(); } var user = await authentication.GetUser(); if (user != null) { return Problem(detail: "User already exists - only one user allowed", statusCode: StatusCodes.Status401Unauthorized); } if (String.IsNullOrEmpty(request.UserName) || String.IsNullOrEmpty(request.Password)) { return Problem(detail: "Empty UserName or Password", statusCode: StatusCodes.Status400BadRequest); } var registerResult = await authentication.Register(request.UserName, request.Password); if (!registerResult.Succeeded) { return Problem(detail: registerResult.Errors.First().Description, statusCode: StatusCodes.Status400BadRequest); } await authentication.Login(request.UserName, request.Password); return Ok(); } /// /// Configures the provider settings for the application /// /// /// This endpoint can only be used when no provider is configured. /// It sets up the initial provider configuration including the API key. /// /// Provider configuration details including provider type and API token /// Provider configured successfully /// Invalid request - missing required fields /// Provider already configured [AllowAnonymous] [Route("SetupProvider")] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task SetupProvider([FromBody] AuthControllerSetupProviderRequest? request) { if (request == null) { return BadRequest(); } if (!String.IsNullOrEmpty(Settings.Get.Provider.ApiKey)) { return StatusCode(401); } var user = await authentication.GetUser(); if (user != null) { return StatusCode(401); } await settings.Update("Provider:Provider", request.Provider); await settings.Update("Provider:ApiKey", request.Token); return Ok(); } /// /// Authenticates a user and creates a new session /// /// /// Validates the provided credentials and creates an authenticated session if valid. /// /// Login credentials /// Authentication successful /// Invalid credentials or missing required fields /// System requires initial setup - no users exist [AllowAnonymous] [Route("Login")] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))] [ProducesResponseType(StatusCodes.Status402PaymentRequired, Type = typeof(ProblemDetails))] public async Task> Login([FromBody] AuthControllerLoginRequest? request) { if (request == null) { return BadRequest(); } var user = await authentication.GetUser(); if (user == null) { return Problem(detail: "Setup required", statusCode: StatusCodes.Status402PaymentRequired); } if (String.IsNullOrEmpty(request.UserName) || String.IsNullOrEmpty(request.Password)) { return Problem(detail: "Invalid credentials", statusCode: StatusCodes.Status400BadRequest); } var result = await authentication.Login(request.UserName, request.Password); if (!result.Succeeded) { return Problem(detail: "Invalid credentials", statusCode: StatusCodes.Status400BadRequest); } return Ok(); } /// /// Ends the current authenticated session /// /// Session terminated successfully [Route("Logout")] [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] public async Task Logout() { await authentication.Logout(); return Ok(); } /// /// Updates the authenticated user's password /// /// /// Requires authentication. Updates the password for the currently logged in user. /// /// Password update request containing the new password /// Password updated successfully /// Invalid request or password validation failed /// Unauthorized - user must be authenticated [Route("Update")] [HttpPost] [Authorize(Policy = "AuthSetting")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task> Update([FromBody] AuthControllerUpdateRequest? request) { if (request == null) { return BadRequest(); } if (String.IsNullOrEmpty(request.UserName) || String.IsNullOrEmpty(request.Password)) { return Problem(detail: "Invalid UserName or Password", statusCode: StatusCodes.Status400BadRequest); } var updateResult = await authentication.Update(request.UserName, request.Password); if (!updateResult.Succeeded) { return Problem(detail: updateResult.Errors.First().Description, statusCode: StatusCodes.Status400BadRequest); } return Ok(); } } /// /// Request model for login and user creation operations /// public class AuthControllerLoginRequest { /// /// Username for authentication /// /// admin [Required] public String? UserName { get; set; } /// /// Password for authentication /// /// MySecurePassword123! [Required] public String? Password { get; set; } } /// /// Request model for configuring the provider settings /// public class AuthControllerSetupProviderRequest { /// /// Provider type identifier /// /// 1 [Required] public Int32 Provider { get; set; } /// /// API token or key for the provider /// /// sk-1234567890abcdef [Required] public String? Token { get; set; } } /// /// Request model for updating user password /// public class AuthControllerUpdateRequest { /// /// Username for the account /// /// myusername [Required] public String? UserName { get; set; } /// /// New password for the user account /// /// NewSecurePassword123! [Required] public String? Password { get; set; } }