fix: login muito tempo parado e mais logs do stripe
All checks were successful
BCards Deployment Pipeline / Run Tests (push) Successful in 2s
BCards Deployment Pipeline / PR Validation (push) Has been skipped
BCards Deployment Pipeline / Build and Push Image (push) Successful in 15m25s
BCards Deployment Pipeline / Deploy to Production (ARM - OCI) (push) Successful in 1m17s
BCards Deployment Pipeline / Deploy to Staging (x86 - Local) (push) Has been skipped
BCards Deployment Pipeline / Cleanup Old Resources (push) Has been skipped
BCards Deployment Pipeline / Deployment Summary (push) Successful in 0s

This commit is contained in:
Ricardo Carneiro 2025-08-31 20:21:56 -03:00
parent 572f1ebf2e
commit 406c298afb
6 changed files with 107 additions and 30 deletions

View File

@ -52,7 +52,7 @@ public class AuthController : Controller
var result = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
if (!result.Succeeded)
{
TempData["Error"] = "Falha na autenticação com Google";
TempData["Error"] = "Falha na autentica<EFBFBD><EFBFBD>o com Google";
return RedirectToAction("Login");
}
@ -82,7 +82,7 @@ public class AuthController : Controller
var result = await HttpContext.AuthenticateAsync(MicrosoftAccountDefaults.AuthenticationScheme);
if (!result.Succeeded)
{
TempData["Error"] = "Falha na autenticação com Microsoft";
TempData["Error"] = "Falha na autentica<EFBFBD><EFBFBD>o com Microsoft";
return RedirectToAction("Login");
}
@ -105,31 +105,16 @@ public class AuthController : Controller
return RedirectToLocal(returnUrl);
}
[HttpGet]
[HttpPost]
[Route("Logout")]
[Authorize]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
// Identifica qual provedor foi usado
var authResult = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var loginProvider = authResult.Principal?.FindFirst("LoginProvider")?.Value;
// Faz logout local primeiro
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
TempData["Success"] = "Logout realizado com sucesso";
// Se foi Microsoft, faz logout completo no provedor
if (loginProvider == "Microsoft")
{
return SignOut(MicrosoftAccountDefaults.AuthenticationScheme);
}
// Se foi Google, faz logout completo no provedor
else if (loginProvider == "Google")
{
return SignOut(GoogleDefaults.AuthenticationScheme);
}
TempData["Success"] = "Você saiu com segurança.";
return RedirectToAction("Index", "Home");
}

View File

@ -34,12 +34,14 @@ public class StripeWebhookController : ControllerBase
try
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
_logger.LogInformation($"Recebido:{json}");
if (string.IsNullOrEmpty(_webhookSecret))
{
_logger.LogWarning("Webhook secret not configured");
return BadRequest("Webhook secret not configured");
}
_logger.LogWarning($"Recebido:{json}");
var stripeSignature = Request.Headers["Stripe-Signature"].FirstOrDefault();
if (string.IsNullOrEmpty(stripeSignature))

View File

@ -0,0 +1,39 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authentication.Cookies;
namespace BCards.Web.Middleware
{
public class OAuthCorrelationErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<OAuthCorrelationErrorHandlerMiddleware> _logger;
public OAuthCorrelationErrorHandlerMiddleware(RequestDelegate next, ILogger<OAuthCorrelationErrorHandlerMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (AuthenticationFailureException ex)
when (ex.Message.Contains("Correlation failed"))
{
_logger.LogWarning(ex, "OAuth correlation failure detected for trace id {TraceId}. This often happens when the user's session expires on the login page before they complete the OAuth flow. Cleaning up and redirecting to login.", context.TraceIdentifier);
// Clean up the failed authentication attempt
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// Redirect to a clean login page with an error message
context.Response.Redirect("/Auth/Login?error=session_expired");
}
}
}
}

View File

@ -173,28 +173,50 @@ builder.Services.AddAuthentication(options =>
{
options.LoginPath = "/Auth/Login";
options.LogoutPath = "/Auth/Logout";
// 🔥 OTIMIZAÇÃO: Timeout de sessão estendido para 8h
options.ExpireTimeSpan = TimeSpan.FromHours(8);
options.SlidingExpiration = true;
// 🔥 CORREÇÃO: Configurações de cookie seguras para evitar problemas de correlação
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.Cookie.SameSite = SameSiteMode.Lax; // Essencial para fluxos OAuth
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Garante HTTPS
})
.AddGoogle(options =>
{
var googleAuth = builder.Configuration.GetSection("Authentication:Google");
options.ClientId = googleAuth["ClientId"] ?? "";
options.ClientSecret = googleAuth["ClientSecret"] ?? "";
options.CallbackPath = "/signin-google"; // Path explícito
options.BackchannelTimeout = TimeSpan.FromSeconds(30);
// 🔥 CORREÇÃO: Handler para falhas remotas (ex: usuário nega permissão)
options.Events = new OAuthEvents
{
OnRemoteFailure = context =>
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Program>>();
logger.LogWarning("Google remote failure: {Failure}", context.Failure?.Message);
context.Response.Redirect("/Auth/Login?error=remote_failure");
context.HandleResponse();
return Task.CompletedTask;
}
};
})
.AddMicrosoftAccount(options =>
{
var msAuth = builder.Configuration.GetSection("Authentication:Microsoft");
options.ClientId = msAuth["ClientId"] ?? "";
options.ClientSecret = msAuth["ClientSecret"] ?? "";
options.CallbackPath = "/signin-microsoft";
options.CallbackPath = "/signin-microsoft"; // Path explícito
options.BackchannelTimeout = TimeSpan.FromSeconds(30);
// Força seleção de conta a cada login
options.AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
options.TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
// 🔥 SOLUÇÃO RADICAL: SEMPRE FORÇAR HTTPS
// 🔥 CORREÇÃO: Handler para falhas remotas e preservação do evento existente
options.Events = new OAuthEvents
{
OnRedirectToAuthorizationEndpoint = context =>
@ -233,6 +255,15 @@ builder.Services.AddAuthentication(options =>
context.Response.Redirect(redirectUri);
return Task.CompletedTask;
},
OnRemoteFailure = context =>
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Program>>();
logger.LogWarning("Microsoft remote failure: {Failure}", context.Failure?.Message);
context.Response.Redirect("/Auth/Login?error=remote_failure");
context.HandleResponse();
return Task.CompletedTask;
}
};
});

View File

@ -15,6 +15,24 @@
<p class="text-muted">Entre na sua conta</p>
</div>
@if (Context.Request.Query.ContainsKey("error"))
{
<div class="alert alert-warning text-center mb-4" role="alert">
@switch (Context.Request.Query["error"].ToString())
{
case "session_expired":
<text>Sua sessão expirou. Por favor, faça login novamente.</text>
break;
case "remote_failure":
<text>Ocorreu um erro durante a autenticação com o provedor. Tente novamente.</text>
break;
default:
<text>Ocorreu um erro de autenticação. Tente novamente.</text>
break;
}
</div>
}
<form asp-controller="Auth" asp-action="LoginWithGoogle" method="post" class="mb-3">
@if (!string.IsNullOrEmpty(returnUrl))
{

View File

@ -170,10 +170,12 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")"
asp-area="" asp-controller="Auth" asp-action="Logout">
<i class="fas fa-sign-out-alt me-1"></i>Sair
</a>
<form asp-area="" asp-controller="Auth" asp-action="Logout" method="post" id="logoutForm" class="d-inline">
@Html.AntiForgeryToken()
<a href="#" onclick="document.getElementById('logoutForm').submit(); return false;" class="nav-link @(ViewBag.IsHomePage == true ? "text-white" : "text-dark")">
<i class="fas fa-sign-out-alt me-1"></i>Sair
</a>
</form>
</li>
}
else