MFATools 10.0.2

dotnet add package MFATools --version 10.0.2
                    
NuGet\Install-Package MFATools -Version 10.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MFATools" Version="10.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MFATools" Version="10.0.2" />
                    
Directory.Packages.props
<PackageReference Include="MFATools" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MFATools --version 10.0.2
                    
#r "nuget: MFATools, 10.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MFATools@10.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MFATools&version=10.0.2
                    
Install as a Cake Addin
#tool nuget:?package=MFATools&version=10.0.2
                    
Install as a Cake Tool

MFATools

.NET Version License NuGet

MFATools es una librería completa en .NET para implementar autenticación de dos factores (2FA/MFA) basada en TOTP (Time-based One-Time Password), junto con utilidades adicionales para hashing de contraseñas y generación de contraseñas seguras. Facilita la generación y verificación de códigos TOTP, la creación de códigos QR para enrolamiento de usuarios, y proporciona proveedores configurables e intercambiables para QR, RNG y tiempo.


📑 Tabla de Contenidos


✨ Características

🔐 Autenticación de Dos Factores (2FA/TOTP)

  • Generación de secretos en Base32 con RNG criptográficamente seguro
  • Generación de códigos TOTP de 6-8 dígitos con múltiples algoritmos (SHA1, SHA256, SHA512, MD5)
  • Verificación de códigos con soporte para discrepancia de tiempo
  • Generación de URIs otpauth:// compatibles con Google Authenticator y similares
  • Generación de códigos QR para enrolamiento fácil
  • Validación de tiempo del servidor contra proveedores NTP/HTTP

🔒 Seguridad de Contraseñas

  • Hashing con BCrypt (factor de trabajo 12)
  • Verificación segura contra timing attacks
  • Generación de contraseñas aleatorias con configuración flexible

🧩 Arquitectura Extensible

  • Proveedores intercambiables para QR, RNG y Tiempo
  • Integración con Dependency Injection de ASP.NET Core
  • Sin dependencias externas para funcionalidad principal (excepto BCrypt y QRCoder)

📦 Instalación

Desde NuGet

dotnet add package MFATools

Desde el código fuente

git clone https://github.com/master-tech-team/MFATools.git
cd MFATools/MFATools
dotnet restore
dotnet build

🚀 Inicio Rápido

1. Registrar servicios en Program.cs o Startup.cs

using MFATools;

var builder = WebApplication.CreateBuilder(args);

// Registrar todos los servicios de MFATools
builder.Services.AddMFAServices(builder.Configuration);

var app = builder.Build();

2. Inyectar y usar en un controlador

using MFATools.Interfaces;

public class AuthController : ControllerBase
{
    private readonly IMFAService _mfaService;
    private readonly IPasswordHasherService _passwordHasher;

    public AuthController(IMFAService mfaService, IPasswordHasherService passwordHasher)
    {
        _mfaService = mfaService;
        _passwordHasher = passwordHasher;
    }

    [HttpPost("enroll")]
    public IActionResult EnrollUser(string username)
    {
        // 1. Generar secreto
        var secret = _mfaService.CreateSecret();
        
        // 2. Generar URI otpauth
        var uri = _mfaService.GetQrText(username, secret);
        
        // 3. Generar QR como data URI
        var qrDataUri = _mfaService.GetQrCodeImageAsDataUri(username, secret);
        
        // 4. Guardar el secreto asociado al usuario (cifrado)
        // ...
        
        return Ok(new { secret, uri, qrDataUri });
    }

    [HttpPost("verify")]
    public IActionResult VerifyCode(string username, string code)
    {
        // 1. Obtener secreto del usuario desde BD
        var secret = GetUserSecret(username);
        
        // 2. Verificar código
        var isValid = _mfaService.VerifyCode(secret, code);
        
        return isValid ? Ok() : Unauthorized();
    }
}

⚙️ Configuración

appsettings.json

{
  "MFA": {
    "Issuer": "MiAplicacion"
  }
}

Registro manual de proveedores

Si deseas personalizar los proveedores:

using MFATools.Providers;
using MFATools.Models;

services.AddSingleton<IQrCodeProvider, QRCoderQRCodeProvider>(); // O ImageChartsQrCodeProvider
services.AddSingleton<IRngProvider, DefaultRngProvider>();        // Criptográficamente seguro
services.AddSingleton<ITimeProvider, NTPTimeProvider>();          // O HttpTimeProvider / LocalMachineTimeProvider
services.AddSingleton<IPasswordHasherService, PasswordHasherService>();

services.AddSingleton<TwoFactorAuth>(sp =>
{
    var issuer = Configuration["MFA:Issuer"];
    var qr = sp.GetRequiredService<IQrCodeProvider>();
    var rng = sp.GetRequiredService<IRngProvider>();
    var time = sp.GetRequiredService<ITimeProvider>();
    
    return new TwoFactorAuth(
        issuer: issuer,
        qrcodeprovider: qr,
        rngprovider: rng,
        timeprovider: time
    );
});

services.AddScoped<IMFAService, MFAService>();

🎯 Funcionalidades

1. Autenticación de Dos Factores (2FA/TOTP)

Generar secreto
// Generar secreto con 80 bits (predeterminado)
var secret = _mfaService.CreateSecret();

// Generar secreto con bits personalizados
var secret128 = _mfaService.CreateSecret(128);

// Generar secreto permitiendo RNG no seguro (no recomendado)
var secretInsecure = _mfaService.CreateSecret(80, CryptoSecureRequirement.AllowInsecure);
Generar código TOTP
// Generar código para el momento actual
var code = _mfaService.GetCode(secret);

// Generar código para una fecha específica
var codeForDate = _mfaService.GetCode(secret, DateTime.UtcNow.AddMinutes(-1));

// Generar código para un timestamp específico
var codeForTimestamp = _mfaService.GetCode(secret, 1234567890);
Verificar código TOTP
// Verificar con discrepancia predeterminada (±1 período = ±30 segundos)
bool isValid = _mfaService.VerifyCode(secret, userCode);

// Verificar con discrepancia personalizada (±2 períodos = ±60 segundos)
bool isValid = _mfaService.VerifyCode(secret, userCode, discrepancy: 2);

// Verificar y obtener el timeslice que coincidió
bool isValid = _mfaService.VerifyCode(secret, userCode, out long timeSlice);

// Verificar para fecha/timestamp específico
bool isValid = _mfaService.VerifyCode(secret, userCode, discrepancy: 1, dateTime: specificDate);
Generar QR para enrolamiento
// Generar URI otpauth
var otpAuthUri = _mfaService.GetQrText("user@example.com", secret);
// Resultado: otpauth://totp/user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MiApp&period=30&algorithm=SHA1&digits=6

// Generar QR como Data URI (tamaño predeterminado: 200px)
var qrDataUri = _mfaService.GetQrCodeImageAsDataUri("user@example.com", secret);
// Resultado: data:image/png;base64,iVBORw0KGgoAAAANSUhEU...

// Generar QR con tamaño personalizado
var qrDataUri300 = _mfaService.GetQrCodeImageAsDataUri("user@example.com", secret, size: 300);
Validar hora del servidor
// Validar hora contra proveedores NTP/HTTP predeterminados (leniency = 5 segundos)
try
{
    _mfaService.EnsureCorrectTime();
    // La hora del servidor es correcta
}
catch (TimeProviderException ex)
{
    // La hora del servidor está desincronizada
    Console.WriteLine(ex.Message);
}

// Validar con leniency personalizada
_mfaService.EnsureCorrectTime(leniency: 10);

// Validar contra proveedores específicos
var timeProviders = new List<ITimeProvider>
{
    new NTPTimeProvider(),
    new HttpTimeProvider()
};
_mfaService.EnsureCorrectTime(timeProviders, leniency: 5);

2. Hashing de Contraseñas

Usando IPasswordHasherService
// Hash de contraseña
string hashedPassword = _passwordHasher.HashPassword("MiContraseña123!");

// Verificar contraseña
bool isCorrect = _passwordHasher.VerifyPassword("MiContraseña123!", hashedPassword);
Usando el helper estático
using MFATools.Helpers;

// Hash de contraseña
string hashedPassword = PasswordHasher.HashPassword("MiContraseña123!");

// Verificar con extensión
bool isCorrect = "MiContraseña123!".VerifyPassword(hashedPassword);

Características:

  • Usa BCrypt con factor de trabajo 12
  • Resistente a rainbow tables y fuerza bruta
  • Salting automático

3. Generación de Contraseñas Aleatorias

using MFATools.Helpers;

// Contraseña con configuración predeterminada (16 caracteres, mayúsculas, minúsculas, números, especiales)
var randomPwd = new RandomPassword();
string password = randomPwd.Next();

// Contraseña de longitud específica
var randomPwd20 = new RandomPassword(passwordLength: 20);
string password20 = randomPwd20.Next();

// Contraseña personalizada con builder pattern
var customPwd = new RandomPassword()
    .IncludeLowercase()
    .IncludeUppercase()
    .IncludeNumeric()
    .IncludeSpecial("!@#$%^&*()")
    .LengthRequired(24)
    .Next();

// Generar grupo de contraseñas
var passwords = new RandomPassword(16).NextGroup(10); // 10 contraseñas

Configuración avanzada:

var settings = new PasswordSettings(
    includeLowercase: true,
    includeUppercase: true,
    includeNumeric: true,
    includeSpecial: true,
    passwordLength: 16,
    maximumAttempts: 10000,
    usingDefaults: false
);

var randomPwd = new RandomPassword(settings);
string password = randomPwd.Next();

Características:

  • Evita más de 2 caracteres idénticos consecutivos
  • Usa RandomNumberGenerator criptográficamente seguro
  • Validación de requisitos (longitud, conjuntos de caracteres)

🔌 Proveedores

QR Code Providers

Provider Descripción Ubicación
QRCoderQRCodeProvider Generación local con QRCoder (predeterminado en DI) Models/QRCoderQRCodeProvider.cs
QrServerQrCodeProvider API externa api.qrserver.com Providers/Qr/QrServerQrCodeProvider.cs
ImageChartsQrCodeProvider API externa image-charts.com Providers/Qr/ImageChartsQrCodeProvider.cs
QRicketQrCodeProvider API externa qrickit.com Providers/Qr/QRicketQrCodeProvider.cs

Interfaz:

public interface IQrCodeProvider
{
    byte[] GetQrCodeImage(string text, int size);
    string GetMimeType();
}

Uso personalizado:

services.AddSingleton<IQrCodeProvider, ImageChartsQrCodeProvider>();

RNG Providers

Provider Descripción Criptográficamente Seguro
DefaultRngProvider Usa RandomNumberGenerator de .NET (predeterminado) ✅ Sí
HashRngProvider Basado en hash SHA256 ❌ No
PrngProvider Generador pseudo-aleatorio ❌ No

Interfaz:

public interface IRngProvider
{
    bool IsCryptographicallySecure { get; }
    byte[] GetRandomBytes(int bytes);
}

Recomendación: Usar siempre DefaultRngProvider en producción.


Time Providers

Provider Descripción Uso Recomendado
LocalMachineTimeProvider Hora del sistema (predeterminado en DI) Desarrollo/testing
NTPTimeProvider Consulta servidor NTP pool.ntp.org Producción (servidores sin sincronización confiable)
HttpTimeProvider Consulta API worldtimeapi.org Alternativa a NTP

Interfaz:

public interface ITimeProvider
{
    Task<DateTime> GetTimeAsync();
}

Uso múltiple para validación:

var timeProviders = new[]
{
    new NTPTimeProvider(),
    new HttpTimeProvider()
};

// Validar que la hora local no difiera más de 5 segundos de los proveedores externos
_twoFactorAuth.EnsureCorrectTime(timeProviders, leniency: 5);

📚 API Reference

IMFAService

Servicio principal para autenticación 2FA/TOTP.

Métodos

Generación de secretos:

string CreateSecret()
string CreateSecret(int bits)
string CreateSecret(int bits, CryptoSecureRequirement cryptoSecureRequirement)

Generación de códigos:

string GetCode(string secret)
string GetCode(string secret, DateTime dateTime)
string GetCode(string secret, long timestamp)

Verificación de códigos:

bool VerifyCode(string secret, string code)
bool VerifyCode(string secret, string code, int discrepancy)
bool VerifyCode(string secret, string code, int discrepancy, DateTime dateTime)
bool VerifyCode(string secret, string code, int discrepancy, DateTime dateTime, out long timeSlice)
bool VerifyCode(string secret, string code, int discrepancy, long timestamp)
bool VerifyCode(string secret, string code, int discrepancy, long timestamp, out long timeSlice)
bool VerifyCode(string secret, string code, int discrepancy, out long timeSlice)
bool VerifyCode(string secret, string code, out long timeSlice)

Generación de QR:

string GetQrText(string label, string secret)
string GetQrCodeImageAsDataUri(string label, string secret)
string GetQrCodeImageAsDataUri(string label, string secret, int size)

Validación de tiempo:

void EnsureCorrectTime(int leniency = 5)
void EnsureCorrectTime(IEnumerable<ITimeProvider> timeproviders)
void EnsureCorrectTime(IEnumerable<ITimeProvider> timeproviders, int leniency)

IPasswordHasherService

Servicio para hashing seguro de contraseñas.

public interface IPasswordHasherService
{
    string HashPassword(string plainPassword);
    bool VerifyPassword(string submittedPassword, string hashedPassword);
}

TwoFactorAuth

Clase principal que implementa la lógica TOTP. Se puede usar directamente o a través de IMFAService.

Propiedades
  • string Issuer - Emisor del token TOTP
  • int Digits - Número de dígitos (predeterminado: 6)
  • int Period - Período de validez en segundos (predeterminado: 30)
  • Algorithm Algorithm - Algoritmo hash (predeterminado: SHA1)
  • IQrCodeProvider QrCodeProvider - Proveedor de QR
  • IRngProvider RngProvider - Proveedor de RNG
  • ITimeProvider TimeProvider - Proveedor de tiempo
Constantes
  • DEFAULTDIGITS = 6
  • DEFAULTPERIOD = 30
  • DEFAULTLENIENCY = 5
  • DEFAULTALGORITHM = Algorithm.SHA1
  • DEFAULTDISCREPANCY = 1
  • DEFAULTSECRETBITS = 80
  • DEFAULTQRCODESIZE = 200
Constructor
public TwoFactorAuth(
    string issuer = null,
    int digits = DEFAULTDIGITS,
    int period = DEFAULTPERIOD,
    Algorithm algorithm = Algorithm.SHA1,
    IQrCodeProvider qrcodeprovider = null,
    IRngProvider rngprovider = null,
    ITimeProvider timeprovider = null
)

Enums

Algorithm
public enum Algorithm
{
    SHA1,
    SHA256,
    SHA512,
    MD5
}
CryptoSecureRequirement
public enum CryptoSecureRequirement
{
    RequireSecure,    // Requiere RNG criptográficamente seguro
    AllowInsecure     // Permite RNG no seguro
}
ErrorCorrectionLevel
public enum ErrorCorrectionLevel
{
    Low = 'L',        // 7% restaurable
    Medium = 'M',     // 15% restaurable
    Quartile = 'Q',   // 25% restaurable
    High = 'H'        // 30% restaurable
}

💡 Ejemplos de Uso

Ejemplo completo: Enrolamiento y verificación

public class MFAController : ControllerBase
{
    private readonly IMFAService _mfaService;
    private readonly IUserRepository _userRepo;

    public MFAController(IMFAService mfaService, IUserRepository userRepo)
    {
        _mfaService = mfaService;
        _userRepo = userRepo;
    }

    // Paso 1: Usuario solicita enrolamiento 2FA
    [HttpPost("api/mfa/enroll")]
    public async Task<IActionResult> EnrollMFA()
    {
        var userId = GetCurrentUserId();
        
        // Generar secreto
        var secret = _mfaService.CreateSecret();
        
        // Guardar secreto cifrado en BD
        await _userRepo.SaveEncryptedSecret(userId, secret);
        
        // Generar QR
        var email = await _userRepo.GetUserEmail(userId);
        var qrDataUri = _mfaService.GetQrCodeImageAsDataUri(email, secret, size: 300);
        
        return Ok(new 
        { 
            qrCode = qrDataUri,
            manualEntryKey = secret
        });
    }

    // Paso 2: Usuario verifica escaneó correctamente enviando primer código
    [HttpPost("api/mfa/verify-enrollment")]
    public async Task<IActionResult> VerifyEnrollment([FromBody] string code)
    {
        var userId = GetCurrentUserId();
        var secret = await _userRepo.GetEncryptedSecret(userId);
        
        if (_mfaService.VerifyCode(secret, code))
        {
            await _userRepo.EnableMFA(userId);
            return Ok(new { message = "2FA activado exitosamente" });
        }
        
        return BadRequest(new { error = "Código inválido" });
    }

    // Paso 3: Login con 2FA
    [HttpPost("api/auth/login-mfa")]
    public async Task<IActionResult> LoginWithMFA([FromBody] LoginMFARequest request)
    {
        // Validar usuario y contraseña primero
        var user = await _userRepo.ValidateCredentials(request.Email, request.Password);
        if (user == null)
            return Unauthorized();

        // Si tiene 2FA habilitado, verificar código
        if (user.MFAEnabled)
        {
            var secret = await _userRepo.GetEncryptedSecret(user.Id);
            
            if (!_mfaService.VerifyCode(secret, request.MFACode))
                return Unauthorized(new { error = "Código 2FA inválido" });
        }

        // Generar JWT o sesión
        var token = GenerateAuthToken(user);
        return Ok(new { token });
    }
}

Ejemplo: Validación de hora del servidor en startup

// En Program.cs o Startup.cs
public void Configure(IApplicationBuilder app, IMFAService mfaService)
{
    // Validar hora del servidor al iniciar la aplicación
    try
    {
        mfaService.EnsureCorrectTime(leniency: 10);
        Console.WriteLine("✅ Hora del servidor sincronizada correctamente");
    }
    catch (TimeProviderException ex)
    {
        Console.WriteLine($"⚠️ ADVERTENCIA: {ex.Message}");
        Console.WriteLine("Los códigos TOTP pueden no funcionar correctamente");
    }

    // Resto de configuración...
}

🏗️ Arquitectura

Diagrama de componentes

MFATools
├── Models
│   ├── TwoFactorAuth.cs          # Lógica principal TOTP
│   ├── DefaultProviders.cs        # Proveedores predeterminados
│   └── QRCoderQRCodeProvider.cs   # Implementación QR local
├── Services
│   ├── MFAService.cs              # Fachada para DI
│   └── PasswordHasherService.cs   # Servicio de hashing
├── Interfaces
│   ├── IMFAService.cs
│   ├── IPasswordHasherService.cs
│   ├── IQrCodeProvider.cs
│   ├── IRngProvider.cs
│   └── ITimeProvider.cs
├── Providers
│   ├── Qr/                        # Implementaciones QR
│   ├── Rng/                       # Implementaciones RNG
│   └── Time/                      # Implementaciones Time
├── Helpers
│   ├── PasswordHasher.cs          # Helper estático BCrypt
│   └── RandomPassword.cs          # Generador de contraseñas
├── Enums
│   ├── Algorithm.cs
│   ├── CryptoSecureRequirement.cs
│   └── ErrorCorrectionLevel.cs
├── Exceptions
│   └── TimeProviderException.cs
└── DependencyInjection.cs         # Extensiones para IServiceCollection

Flujo de ejecución típico

  1. Registro DI: AddMFAServices() registra todos los servicios y proveedores
  2. Inyección: Controladores/servicios reciben IMFAService y IPasswordHasherService
  3. Delegación: MFAService delega a TwoFactorAuth
  4. Proveedores: TwoFactorAuth usa proveedores inyectados (IQrCodeProvider, IRngProvider, ITimeProvider)

Lifetime recomendados

Servicio/Proveedor Lifetime Razón
IQrCodeProvider Singleton Sin estado
IRngProvider Singleton Sin estado
ITimeProvider Singleton Sin estado
TwoFactorAuth Singleton Sin estado por usuario
IMFAService Scoped Apropiado para web (request-scoped)
IPasswordHasherService Singleton Sin estado

🔒 Seguridad

Mejores prácticas

  1. Almacenamiento de secretos:

    • NO guardar secretos en texto plano
    • ✅ Cifrar secretos antes de almacenar en BD
    • ✅ Usar Azure Key Vault, AWS Secrets Manager, o similar
  2. RNG:

    • ✅ Usar siempre DefaultRngProvider (criptográficamente seguro)
    • ❌ Nunca usar PrngProvider o HashRngProvider en producción
  3. Sincronización de tiempo:

    • ✅ Sincronizar servidores con NTP
    • ✅ Validar hora del servidor en startup con EnsureCorrectTime()
    • ✅ Usar NTPTimeProvider si la hora local no es confiable
  4. Discrepancia de tiempo:

    • ✅ Usar discrepancia de 1-2 períodos (30-60 segundos)
    • ❌ No usar discrepancias muy grandes (reduce seguridad)
  5. Códigos de recuperación:

    • ✅ Generar códigos de recuperación para usuarios que pierden acceso
    • ✅ Guardar códigos hasheados con BCrypt
  6. Hashing de contraseñas:

    • ✅ BCrypt con factor de trabajo 12 (predeterminado)
    • ✅ Ajustar factor de trabajo según capacidad del servidor

Ejemplo: Cifrado de secretos

using System.Security.Cryptography;
using System.Text;

public class SecretEncryption
{
    private readonly byte[] _encryptionKey;

    public SecretEncryption(IConfiguration config)
    {
        // Cargar clave de cifrado desde Key Vault o configuración segura
        _encryptionKey = Convert.FromBase64String(config["Encryption:Key"]);
    }

    public string Encrypt(string plainSecret)
    {
        using var aes = Aes.Create();
        aes.Key = _encryptionKey;
        aes.GenerateIV();

        using var encryptor = aes.CreateEncryptor();
        var plainBytes = Encoding.UTF8.GetBytes(plainSecret);
        var encryptedBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);

        // Combinar IV + datos cifrados
        var result = new byte[aes.IV.Length + encryptedBytes.Length];
        Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
        Buffer.BlockCopy(encryptedBytes, 0, result, aes.IV.Length, encryptedBytes.Length);

        return Convert.ToBase64String(result);
    }

    public string Decrypt(string encryptedSecret)
    {
        var fullData = Convert.FromBase64String(encryptedSecret);

        using var aes = Aes.Create();
        aes.Key = _encryptionKey;

        // Extraer IV y datos
        var iv = new byte[aes.IV.Length];
        var encryptedBytes = new byte[fullData.Length - iv.Length];
        Buffer.BlockCopy(fullData, 0, iv, 0, iv.Length);
        Buffer.BlockCopy(fullData, iv.Length, encryptedBytes, 0, encryptedBytes.Length);

        aes.IV = iv;
        using var decryptor = aes.CreateDecryptor();
        var decryptedBytes = decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);

        return Encoding.UTF8.GetString(decryptedBytes);
    }
}

🧪 Testing

Testing unitario con proveedores falsos

public class FakeTimeProvider : ITimeProvider
{
    private readonly DateTime _fixedTime;

    public FakeTimeProvider(DateTime fixedTime)
    {
        _fixedTime = fixedTime;
    }

    public Task<DateTime> GetTimeAsync() => Task.FromResult(_fixedTime);
}

public class FakeRngProvider : IRngProvider
{
    public bool IsCryptographicallySecure => false;

    public byte[] GetRandomBytes(int bytes)
    {
        // Retornar bytes predecibles para testing
        return Enumerable.Repeat((byte)0x42, bytes).ToArray();
    }
}

[Test]
public void VerifyCode_WithFixedTime_ReturnsTrue()
{
    // Arrange
    var fixedTime = new DateTime(2025, 1, 1, 12, 0, 0, DateTimeKind.Utc);
    var fakeTimeProvider = new FakeTimeProvider(fixedTime);
    
    var tfa = new TwoFactorAuth(
        issuer: "Test",
        timeprovider: fakeTimeProvider
    );

    var secret = tfa.CreateSecret();
    var code = tfa.GetCode(secret, fixedTime);

    // Act
    var isValid = tfa.VerifyCode(secret, code);

    // Assert
    Assert.IsTrue(isValid);
}

Testing de integración

[TestClass]
public class MFAServiceIntegrationTests
{
    private ServiceProvider _serviceProvider;
    private IMFAService _mfaService;

    [TestInitialize]
    public void Setup()
    {
        var services = new ServiceCollection();
        var config = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string>
            {
                ["MFA:Issuer"] = "TestApp"
            })
            .Build();

        services.AddMFAServices(config);
        _serviceProvider = services.BuildServiceProvider();
        _mfaService = _serviceProvider.GetRequiredService<IMFAService>();
    }

    [TestMethod]
    public void EndToEnd_CreateSecretAndVerifyCode_Success()
    {
        // Arrange
        var secret = _mfaService.CreateSecret();

        // Act
        var code = _mfaService.GetCode(secret);
        var isValid = _mfaService.VerifyCode(secret, code);

        // Assert
        Assert.IsTrue(isValid);
    }

    [TestCleanup]
    public void Cleanup()
    {
        _serviceProvider?.Dispose();
    }
}

🤝 Contribución

¡Las contribuciones son bienvenidas! Para contribuir:

  1. Fork el repositorio
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. Abre un Pull Request

Lineamientos

  • Seguir convenciones de código C# y .NET
  • Agregar tests unitarios para nuevas funcionalidades
  • Documentar APIs públicas con XML comments
  • Actualizar README si se agregan features importantes

📄 Licencia

Este proyecto está licenciado bajo la Licencia MIT. Ver el archivo LICENSE para más detalles.


📞 Contacto y Soporte


🙏 Agradecimientos

  • QRCoder - Generación local de códigos QR
  • BCrypt.Net-Next - Hashing seguro de contraseñas
  • Inspirado en implementaciones TOTP estándar (RFC 6238)

📝 Changelog

v10.0.1 (Actual)

  • Primera versión pública
  • Generación y verificación de códigos TOTP
  • Creación de secretos y URIs otpauth
  • Generación de códigos QR para enrolamiento
  • Proveedores configurables para QR, RNG y tiempo
  • Integración con Dependency Injection
  • Hashing de contraseñas con BCrypt
  • Generación de contraseñas aleatorias
  • Documentación completa y ejemplos

¡Gracias por usar MFATools! 🚀🔐

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.2 702 11/20/2025
10.0.1 698 11/20/2025
10.0.0 577 11/13/2025

v10.0.0:
    - Primera versión pública
- Generación y verificación de códigos TOTP
- Creación de secretos y URIs otpauth
- Generación de códigos QR para enrolamiento
- Proveedores configurables para QR, RNG y tiempo
- Integración con Dependency Injection
- Ejemplo de uso y documentación básica