Feat: Cambios Varios 2
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
using Google.Apis.Auth;
|
||||
using OtpNet;
|
||||
using SIGCM.Application.DTOs;
|
||||
using SIGCM.Application.Interfaces;
|
||||
using SIGCM.Domain.Entities;
|
||||
using SIGCM.Domain.Interfaces;
|
||||
using System.Text;
|
||||
|
||||
namespace SIGCM.Infrastructure.Services;
|
||||
|
||||
@@ -10,18 +15,149 @@ public class AuthService : IAuthService
|
||||
|
||||
public AuthService(IUserRepository userRepo, ITokenService tokenService)
|
||||
{
|
||||
_userRepo = userRepo;
|
||||
_tokenService = tokenService;
|
||||
_userRepo = userRepo;
|
||||
_tokenService = tokenService;
|
||||
}
|
||||
|
||||
public async Task<string?> LoginAsync(string username, string password)
|
||||
// Inicio de sesión estándar con usuario y contraseña
|
||||
public async Task<AuthResult> LoginAsync(string username, string password)
|
||||
{
|
||||
var user = await _userRepo.GetByUsernameAsync(username);
|
||||
if (user == null) return null;
|
||||
|
||||
bool valid = BCrypt.Net.BCrypt.Verify(password, user.PasswordHash);
|
||||
if (!valid) return null;
|
||||
if (user == null) return new AuthResult { Success = false, ErrorMessage = "Credenciales inválidas" };
|
||||
|
||||
return _tokenService.GenerateToken(user);
|
||||
// Verificación de bloqueo de cuenta
|
||||
if (user.LockoutEnd.HasValue && user.LockoutEnd.Value > DateTime.UtcNow)
|
||||
return new AuthResult { Success = false, ErrorMessage = "Cuenta bloqueada temporalmente", IsLockedOut = true };
|
||||
|
||||
// Verificación de cuenta activa
|
||||
if (!user.IsActive)
|
||||
return new AuthResult { Success = false, ErrorMessage = "Cuenta desactivada" };
|
||||
|
||||
// Verificación de contraseña
|
||||
bool valid = BCrypt.Net.BCrypt.Verify(password, user.PasswordHash);
|
||||
if (!valid)
|
||||
{
|
||||
user.FailedLoginAttempts++;
|
||||
if (user.FailedLoginAttempts >= 5) user.LockoutEnd = DateTime.UtcNow.AddMinutes(15);
|
||||
await _userRepo.UpdateAsync(user);
|
||||
return new AuthResult { Success = false, ErrorMessage = "Credenciales inválidas" };
|
||||
}
|
||||
|
||||
// Si MFA está activo, no devolver token aún, pedir verificación
|
||||
if (user.IsMfaEnabled)
|
||||
{
|
||||
return new AuthResult { Success = true, RequiresMfa = true };
|
||||
}
|
||||
|
||||
// Éxito: Reiniciar intentos y generar token
|
||||
user.FailedLoginAttempts = 0;
|
||||
user.LockoutEnd = null;
|
||||
user.LastLogin = DateTime.UtcNow;
|
||||
await _userRepo.UpdateAsync(user);
|
||||
|
||||
return new AuthResult
|
||||
{
|
||||
Success = true,
|
||||
Token = _tokenService.GenerateToken(user),
|
||||
RequiresPasswordChange = user.MustChangePassword
|
||||
};
|
||||
}
|
||||
|
||||
// Registro de nuevos usuarios (Public Web)
|
||||
public async Task<AuthResult> RegisterAsync(string username, string email, string password)
|
||||
{
|
||||
if (await _userRepo.GetByUsernameAsync(username) != null)
|
||||
return new AuthResult { Success = false, ErrorMessage = "El usuario ya existe" };
|
||||
|
||||
if (await _userRepo.GetByEmailAsync(email) != null)
|
||||
return new AuthResult { Success = false, ErrorMessage = "El email ya está registrado" };
|
||||
|
||||
var user = new User
|
||||
{
|
||||
Username = username,
|
||||
Email = email,
|
||||
PasswordHash = BCrypt.Net.BCrypt.HashPassword(password),
|
||||
Role = "User", // Rol por defecto para la web pública
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
MustChangePassword = false
|
||||
};
|
||||
|
||||
await _userRepo.CreateAsync(user);
|
||||
return new AuthResult { Success = true, Token = _tokenService.GenerateToken(user) };
|
||||
}
|
||||
|
||||
// Login mediante Google OAuth
|
||||
public async Task<AuthResult> GoogleLoginAsync(string idToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var payload = await GoogleJsonWebSignature.ValidateAsync(idToken);
|
||||
var user = await _userRepo.GetByGoogleIdAsync(payload.Subject)
|
||||
?? await _userRepo.GetByEmailAsync(payload.Email);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
// Auto-registro mediante Google
|
||||
user = new User
|
||||
{
|
||||
Username = payload.Email.Split('@')[0],
|
||||
Email = payload.Email,
|
||||
GoogleId = payload.Subject,
|
||||
PasswordHash = "OAUTH_LOGIN_" + Guid.NewGuid().ToString(), // Hash dummy
|
||||
Role = "User",
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
MustChangePassword = false
|
||||
};
|
||||
user.Id = await _userRepo.CreateAsync(user);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(user.GoogleId))
|
||||
{
|
||||
// Vincular cuenta existente con Google
|
||||
user.GoogleId = payload.Subject;
|
||||
await _userRepo.UpdateAsync(user);
|
||||
}
|
||||
|
||||
if (user.IsMfaEnabled) return new AuthResult { Success = true, RequiresMfa = true };
|
||||
|
||||
return new AuthResult { Success = true, Token = _tokenService.GenerateToken(user) };
|
||||
}
|
||||
catch (InvalidJwtException)
|
||||
{
|
||||
return new AuthResult { Success = false, ErrorMessage = "Token de Google inválido" };
|
||||
}
|
||||
}
|
||||
|
||||
// Genera un secreto para configurar MFA con aplicaciones tipo Google Authenticator
|
||||
public async Task<string> GenerateMfaSecretAsync(int userId)
|
||||
{
|
||||
var user = await _userRepo.GetByIdAsync(userId);
|
||||
if (user == null) throw new Exception("Usuario no encontrado");
|
||||
|
||||
var secretBytes = KeyGeneration.GenerateRandomKey(20);
|
||||
var secret = Base32Encoding.ToString(secretBytes);
|
||||
|
||||
user.MfaSecret = secret;
|
||||
await _userRepo.UpdateAsync(user);
|
||||
|
||||
return secret;
|
||||
}
|
||||
|
||||
// Verifica el código TOTP ingresado por el usuario
|
||||
public async Task<bool> VerifyMfaCodeAsync(int userId, string code)
|
||||
{
|
||||
var user = await _userRepo.GetByIdAsync(userId);
|
||||
if (user == null || string.IsNullOrEmpty(user.MfaSecret)) return false;
|
||||
|
||||
var totp = new Totp(Base32Encoding.ToBytes(user.MfaSecret));
|
||||
return totp.VerifyTotp(code, out _, new VerificationWindow(1, 1));
|
||||
}
|
||||
|
||||
// Activa o desactiva MFA para el usuario
|
||||
public async Task EnableMfaAsync(int userId, bool enabled)
|
||||
{
|
||||
var user = await _userRepo.GetByIdAsync(userId);
|
||||
if (user == null) return;
|
||||
user.IsMfaEnabled = enabled;
|
||||
await _userRepo.UpdateAsync(user);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user