Authentication implementation
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
@@ -7,28 +7,109 @@ namespace WebApp.Authentication
|
||||
{
|
||||
public class AuthController : Controller
|
||||
{
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> CookieLogin(string email, string password)
|
||||
private readonly AuthenticationService _authService;
|
||||
private readonly LoginRateLimitService _rateLimitService;
|
||||
private readonly ILogger<AuthController> _logger;
|
||||
|
||||
public AuthController(
|
||||
AuthenticationService authService,
|
||||
LoginRateLimitService rateLimitService,
|
||||
ILogger<AuthController> logger)
|
||||
{
|
||||
// Based on: https://www.codeproject.com/articles/Understanding-authentication-in-Blazor-and-ASP-NET
|
||||
// TODO: Fix this up
|
||||
// Generate the claims
|
||||
var claims = new List<Claim>();
|
||||
claims.Add(new Claim(ClaimTypes.Name, "John Patton"));
|
||||
claims.Add(new Claim(ClaimTypes.Role, "Contributor"));
|
||||
|
||||
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Auth"));
|
||||
|
||||
await HttpContext.SignInAsync("Auth", principal).ConfigureAwait(false);
|
||||
|
||||
return Redirect("/");
|
||||
_authService = authService;
|
||||
_rateLimitService = rateLimitService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpGet] // Support both for navigation from Blazor
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> CookieLogin(
|
||||
string email,
|
||||
string password,
|
||||
bool rememberMe = false)
|
||||
{
|
||||
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);
|
||||
|
||||
TempData["LoginError"] = $"Too many failed attempts. Try again in {remaining?.Minutes ?? 15} minutes.";
|
||||
return Redirect("/login");
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
TempData["LoginError"] = "Invalid email or password.";
|
||||
return Redirect("/login");
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
return Redirect("/");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error during login process");
|
||||
TempData["LoginError"] = "An error occurred. Please try again.";
|
||||
return Redirect("/login");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> CookieLogout()
|
||||
{
|
||||
await HttpContext.SignOutAsync("Auth").ConfigureAwait(false);
|
||||
var userEmail = User.FindFirst(ClaimTypes.Email)?.Value;
|
||||
|
||||
await HttpContext.SignOutAsync("Auth");
|
||||
|
||||
_logger.LogInformation("User {Email} logged out", userEmail);
|
||||
|
||||
return Redirect("/login");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user