Files
chapter-organizer/WebApp/Authentication/AuthController.cs
T
poprhythm 6acbc4e852 Enhance authentication flow by adding return URL support
This commit updates the authentication process to include a return URL parameter, allowing users to be redirected back to their original page after logging in. Changes were made to the AuthController, Login component, and Routes component to handle the return URL appropriately. Additionally, improvements were made to the TeamScheduler and TeamSchedulerSolution classes for better team and student management. These enhancements improve user experience and navigation within the application.
2026-01-11 13:13:24 -05:00

132 lines
5.0 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
namespace WebApp.Authentication
{
public class AuthController : Controller
{
private readonly AuthenticationService _authService;
private readonly LoginRateLimitService _rateLimitService;
private readonly ILogger<AuthController> _logger;
public AuthController(
AuthenticationService authService,
LoginRateLimitService rateLimitService,
ILogger<AuthController> logger)
{
_authService = authService;
_rateLimitService = rateLimitService;
_logger = logger;
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> CookieLogin(
[FromForm] string email,
[FromForm] string password,
[FromForm] bool rememberMe = false,
[FromForm] string? returnUrl = null)
{
try
{
// Get client IP
var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
// Check rate limiting
if (_rateLimitService.IsLockedOut(ipAddress))
{
var remaining = _rateLimitService.GetRemainingLockoutTime(ipAddress);
_logger.LogWarning(
"Login attempt from locked out IP: {IpAddress}. Remaining: {Remaining}",
ipAddress, remaining);
var errorMsg = Uri.EscapeDataString($"Too many failed attempts. Try again in {remaining?.Minutes ?? 15} minutes.");
var redirectUrl = string.IsNullOrEmpty(returnUrl)
? $"/login?error={errorMsg}"
: $"/login?error={errorMsg}&returnUrl={Uri.EscapeDataString(returnUrl)}";
return Redirect(redirectUrl);
}
// Validate credentials
var result = _authService.ValidateCredentials(email, password);
if (!result.IsSuccess)
{
// Record failed attempt
_rateLimitService.RecordFailedAttempt(ipAddress);
_logger.LogWarning(
"Failed login attempt for {Email} from {IpAddress}",
email, ipAddress);
var redirectUrl = string.IsNullOrEmpty(returnUrl)
? "/login?error=Invalid%20email%20or%20password."
: $"/login?error=Invalid%20email%20or%20password.&returnUrl={Uri.EscapeDataString(returnUrl)}";
return Redirect(redirectUrl);
}
// Success - clear rate limit tracking
_rateLimitService.RecordSuccessfulLogin(ipAddress);
// Create claims
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, result.DisplayName!),
new Claim(ClaimTypes.Email, result.Email!),
new Claim(ClaimTypes.Role, result.Role!)
};
var claimsIdentity = new ClaimsIdentity(claims, "Auth");
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
// Configure auth properties
var authProperties = new AuthenticationProperties
{
IsPersistent = rememberMe,
ExpiresUtc = rememberMe
? DateTimeOffset.UtcNow.AddDays(30)
: DateTimeOffset.UtcNow.AddMinutes(20)
};
await HttpContext.SignInAsync("Auth", claimsPrincipal, authProperties);
_logger.LogInformation(
"Successful login for {Email} ({Role}) from {IpAddress}",
result.Email, result.Role, ipAddress);
// Validate return URL is local to prevent open redirect attacks
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return Redirect("/");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during login process");
TempData["LoginError"] = "An error occurred. Please try again.";
var redirectUrl = string.IsNullOrEmpty(returnUrl)
? "/login"
: $"/login?returnUrl={Uri.EscapeDataString(returnUrl)}";
return Redirect(redirectUrl);
}
}
[HttpPost]
[Authorize]
public async Task<IActionResult> CookieLogout()
{
var userEmail = User.FindFirst(ClaimTypes.Email)?.Value;
await HttpContext.SignOutAsync("Auth");
_logger.LogInformation("User {Email} logged out", userEmail);
return Redirect("/login");
}
}
}