fix: authcontroller
This commit is contained in:
parent
b8ab7948a9
commit
c59e6711c0
@ -7,6 +7,9 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using QRRapidoApp.Models.ViewModels;
|
||||
using QRRapidoApp.Services;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
|
||||
namespace QRRapidoApp.Controllers
|
||||
{
|
||||
@ -16,14 +19,17 @@ namespace QRRapidoApp.Controllers
|
||||
private readonly AdDisplayService _adDisplayService;
|
||||
private readonly ILogger<AccountController> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IDataProtector _protector;
|
||||
|
||||
public AccountController(IUserService userService, AdDisplayService adDisplayService,
|
||||
ILogger<AccountController> logger, IConfiguration configuration)
|
||||
ILogger<AccountController> logger, IConfiguration configuration,
|
||||
IDataProtectionProvider dataProtection)
|
||||
{
|
||||
_userService = userService;
|
||||
_adDisplayService = adDisplayService;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
_protector = dataProtection.CreateProtector("OAuth.StateProtection");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -38,11 +44,25 @@ namespace QRRapidoApp.Controllers
|
||||
public IActionResult LoginGoogle(string returnUrl = "/")
|
||||
{
|
||||
var baseUrl = _configuration.GetSection("App:BaseUrl").Value;
|
||||
|
||||
// Criar state com dados criptografados em vez de sessão
|
||||
var stateData = new OAuthStateData
|
||||
{
|
||||
ReturnUrl = returnUrl,
|
||||
Nonce = Guid.NewGuid().ToString(),
|
||||
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
};
|
||||
|
||||
var stateJson = JsonSerializer.Serialize(stateData);
|
||||
var protectedState = _protector.Protect(stateJson);
|
||||
var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState));
|
||||
|
||||
var properties = new AuthenticationProperties
|
||||
{
|
||||
RedirectUri = $"{baseUrl}{Url.Action("GoogleCallback")}",
|
||||
Items = { { "returnUrl", returnUrl } }
|
||||
Items = { { "state", encodedState } }
|
||||
};
|
||||
|
||||
return Challenge(properties, GoogleDefaults.AuthenticationScheme);
|
||||
}
|
||||
|
||||
@ -50,40 +70,84 @@ namespace QRRapidoApp.Controllers
|
||||
public IActionResult LoginMicrosoft(string returnUrl = "/")
|
||||
{
|
||||
var baseUrl = _configuration.GetSection("App:BaseUrl").Value;
|
||||
//var redirectUrl = Url.Action("MicrosoftCallback", "Account", new { returnUrl });
|
||||
var redirectUrl = "";
|
||||
|
||||
if (returnUrl == "/")
|
||||
// Mesmo processo para Microsoft
|
||||
var stateData = new OAuthStateData
|
||||
{
|
||||
redirectUrl = $"{baseUrl}/Account/MicrosoftCallback";
|
||||
}
|
||||
ReturnUrl = returnUrl,
|
||||
Nonce = Guid.NewGuid().ToString(),
|
||||
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
};
|
||||
|
||||
var stateJson = JsonSerializer.Serialize(stateData);
|
||||
var protectedState = _protector.Protect(stateJson);
|
||||
var encodedState = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedState));
|
||||
|
||||
var redirectUrl = returnUrl == "/"
|
||||
? $"{baseUrl}/Account/MicrosoftCallback"
|
||||
: $"{baseUrl}/Account/MicrosoftCallback";
|
||||
|
||||
var properties = new AuthenticationProperties
|
||||
{
|
||||
RedirectUri = redirectUrl,
|
||||
Items = { { "state", encodedState } }
|
||||
};
|
||||
|
||||
var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
|
||||
return Challenge(properties, MicrosoftAccountDefaults.AuthenticationScheme);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GoogleCallback()
|
||||
public async Task<IActionResult> GoogleCallback(string state = null)
|
||||
{
|
||||
return await HandleExternalLoginCallbackAsync(GoogleDefaults.AuthenticationScheme);
|
||||
var returnUrl = await HandleExternalLoginCallbackAsync(GoogleDefaults.AuthenticationScheme, state);
|
||||
return Redirect(returnUrl ?? "/");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> MicrosoftCallback()
|
||||
public async Task<IActionResult> MicrosoftCallback(string state = null)
|
||||
{
|
||||
return await HandleExternalLoginCallbackAsync(MicrosoftAccountDefaults.AuthenticationScheme);
|
||||
var returnUrl = await HandleExternalLoginCallbackAsync(MicrosoftAccountDefaults.AuthenticationScheme, state);
|
||||
return Redirect(returnUrl ?? "/");
|
||||
}
|
||||
|
||||
private async Task<IActionResult> HandleExternalLoginCallbackAsync(string scheme)
|
||||
private async Task<string> HandleExternalLoginCallbackAsync(string scheme, string state = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_adDisplayService.SetViewBagAds(ViewBag);
|
||||
|
||||
// Recuperar returnUrl do state em vez da sessão
|
||||
string returnUrl = "/";
|
||||
if (!string.IsNullOrEmpty(state))
|
||||
{
|
||||
try
|
||||
{
|
||||
var decodedState = Encoding.UTF8.GetString(Convert.FromBase64String(state));
|
||||
var unprotectedState = _protector.Unprotect(decodedState);
|
||||
var stateData = JsonSerializer.Deserialize<OAuthStateData>(unprotectedState);
|
||||
|
||||
// Validar timestamp (não mais que 10 minutos)
|
||||
var maxAge = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - (10 * 60);
|
||||
if (stateData.Timestamp > maxAge)
|
||||
{
|
||||
returnUrl = stateData.ReturnUrl ?? "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning($"OAuth state expired for scheme {scheme}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, $"Failed to decode OAuth state for scheme {scheme}");
|
||||
}
|
||||
}
|
||||
|
||||
var result = await HttpContext.AuthenticateAsync(scheme);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
_logger.LogWarning($"External authentication failed for scheme {scheme}");
|
||||
return RedirectToAction("Login");
|
||||
return "/Account/Login";
|
||||
}
|
||||
|
||||
var email = result.Principal?.FindFirst(ClaimTypes.Email)?.Value;
|
||||
@ -93,7 +157,7 @@ namespace QRRapidoApp.Controllers
|
||||
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(providerId))
|
||||
{
|
||||
_logger.LogWarning($"Missing required claims from {scheme} authentication");
|
||||
return RedirectToAction("Login");
|
||||
return "/Account/Login";
|
||||
}
|
||||
|
||||
// Find or create user
|
||||
@ -127,13 +191,12 @@ namespace QRRapidoApp.Controllers
|
||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
|
||||
new ClaimsPrincipal(claimsIdentity), authProperties);
|
||||
|
||||
var returnUrl = result.Properties?.Items != null && result.Properties.Items.ContainsKey("returnUrl") ? result.Properties?.Items["returnUrl"] : "/";
|
||||
return RedirectToAction("Index", "Home");
|
||||
return returnUrl;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error in external login callback for {scheme}");
|
||||
return RedirectToAction("Login");
|
||||
return "/Account/Login";
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,4 +315,12 @@ namespace QRRapidoApp.Controllers
|
||||
return Json(new { success = false });
|
||||
}
|
||||
}
|
||||
|
||||
// Classe para dados do state
|
||||
public class OAuthStateData
|
||||
{
|
||||
public string ReturnUrl { get; set; } = "/";
|
||||
public string Nonce { get; set; } = "";
|
||||
public long Timestamp { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user