using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using eprintServer.DTOs; using eprintServer.Entities; using eprintServer.Enums; using eprintServer.Requests; using eprintServer.Responses; using eprintServer.Services; namespace eprintServer.Controllers; [ApiController] [Route("api/v1/oauth")] public class OauthCallbackController : ControllerBase { private readonly UserService _userService; private readonly TokenService _tokenService; private readonly GoogleOAuthService _googleOAuthService; private readonly FacebookOAuthService _facebookOAuthService; private readonly LinkedInOAuthService _linkedInOAuthService; public OauthCallbackController( UserService userService, TokenService tokenService, GoogleOAuthService googleOAuthService, FacebookOAuthService facebookOAuthService, LinkedInOAuthService linkedInOAuthService) { _userService = userService; _tokenService = tokenService; _googleOAuthService = googleOAuthService; _facebookOAuthService = facebookOAuthService; _linkedInOAuthService = linkedInOAuthService; } [HttpGet("google/callback")] public async Task> GoogleCallback([FromQuery] string code) { var (user, token, error) = await _googleOAuthService.HandleLoginCallbackAsync(code); if (user == null || token == null) { return Redirect($"https://etbaaonline.com/oauth/success?error={Uri.EscapeDataString(error ?? "Google sign-in failed.")}"); } var result = await CreateAuthResponse(user, GetMinimalDeviceRequest(), Provider.GOOGLE, token); // Extract the JwtResponse from the result if (result.Result is OkObjectResult okResult && okResult.Value is JwtResponse jwtResponse) { var redirectUrl = $"https://etbaaonline.com/oauth/success" + $"?token={Uri.EscapeDataString(jwtResponse.Token)}" + $"&role={Uri.EscapeDataString(jwtResponse.Roles.ToString())}"; return Redirect(redirectUrl); } return Redirect("https://etbaaonline.com/oauth/success?error=auth_failed"); } [HttpGet("facebook/callback")] public async Task> FacebookCallback([FromQuery] string code) { var (user, token, error) = await _facebookOAuthService.HandleLoginCallbackAsync(code); if (user == null || token == null) { return Redirect($"https://etbaaonline.com/oauth/error?error={Uri.EscapeDataString(error ?? "Facebook sign-in failed.")}"); } var result = await CreateAuthResponse(user, GetMinimalDeviceRequest(), Provider.FACEBOOK, token); if (result.Result is OkObjectResult okResult && okResult.Value is JwtResponse jwtResponse) { var redirectUrl = $"https://etbaaonline.com/oauth/success" + $"?token={Uri.EscapeDataString(jwtResponse.Token)}" + $"&role={Uri.EscapeDataString(jwtResponse.Roles.ToString())}"; return Redirect(redirectUrl); } return Redirect("https://etbaaonline.com/oauth/error?error=auth_failed"); } [HttpGet("linkedin/callback")] public async Task> LinkedInCallback([FromQuery] string code) { var (user, token, error) = await _linkedInOAuthService.HandleLoginCallbackAsync(code); if (user == null || token == null) { return Redirect($"https://etbaaonline.com/oauth/error?error={Uri.EscapeDataString(error ?? "LinkedIn sign-in failed.")}"); } var result = await CreateAuthResponse(user, GetMinimalDeviceRequest(), Provider.LINKEDIN, token); if (result.Result is OkObjectResult okResult && okResult.Value is JwtResponse jwtResponse) { var redirectUrl = $"https://etbaaonline.com/oauth/success" + $"?token={Uri.EscapeDataString(jwtResponse.Token)}" + $"&role={Uri.EscapeDataString(jwtResponse.Roles.ToString())}"; return Redirect(redirectUrl); } return Redirect("https://etbaaonline.com/oauth/error?error=auth_failed"); } // --- Helper Methods --- // Create a minimal device request for OAuth logins where the client may not have provided full device info private SignInRequest GetMinimalDeviceRequest() { return new SignInRequest { DeviceType = "Web", DeviceId = "OAuth", Ip = HttpContext.Connection.RemoteIpAddress?.ToString() }; } private async Task> CreateAuthResponse(User user, SignInRequest deviceRequest, Provider provider, string? existingToken = null) { var existingUser = await _userService.FindByEmailAsync(user.Email); if (existingUser == null) { var userCount = await _userService.GetUserCountAsync(); var username = _userService.GenerateUsername(user.Email, userCount); var newUser = new User { Name = user.Email, Email = user.Email, Username = username, Password = "aze", Active = true, Provider = provider }; existingUser = await _userService.SaveUserAsync(newUser); } // Use the DB user going forward user = existingUser; var accessToken = existingToken ?? _tokenService.CreateToken(user); var refreshToken = "placeholder-refresh-token"; var device = new UserDevice { DeviceType = deviceRequest.DeviceType ?? "Unknown", DeviceId = deviceRequest.DeviceId ?? Guid.NewGuid().ToString(), Ip = deviceRequest.Ip, LastJwt = accessToken, PushNotificationToken = deviceRequest.Tokendevice }; user.Device = device; var updatedUser = await _userService.UpdateUserAsync(user); if (updatedUser.Device == null) { // If device update fails for some reason (e.g., DB constraint), return an error return StatusCode(StatusCodes.Status406NotAcceptable, "Failed to save device information."); } var response = new JwtResponse( accessToken, refreshToken, user.Role, updatedUser.Device.DeviceId, updatedUser.Device.DeviceType, updatedUser.Device.Ip, DateTime.UtcNow.AddDays(7) ); return Ok(response); } }