Skip to content

farhadzm/totp-generator

Repository files navigation

You can generate Totp code based on time, UserSecurityStamp and UserIdentifier and ExpirationTime.

You can use this package instead of Rfc6238AuthenticationService class for generating totp code with identity.

NuGet:

<PackageReference Include="TotpGenerator" Version="1.0.2" />

image

Usage:

//Generate totp code
var totpCode = TotpService.GenerateCode(user.SecurityStamp.ToString(), modifier);
//Verify totp code
var isValid = TotpService.ValidateCode(
    user.SecurityStamp.ToString(), 
    totpCode, 
    modifier, 
    _userOption.TwoFactorTotpExpiration);

If you want to modify the TOTP code method generated by Identity, you need to follow these steps:

First, create a class that inherits from the TotpSecurityStampBasedTokenProvider class. Then, you should override the GenerateAsync and ValidateAsync methods and use TotpService instead of Rfc6238AuthenticationService.

GenerateAsync method:

public override async Task<string> GenerateAsync(
    string purpose,
    UserManager<ApplicationUser> manager,
    ApplicationUser user)
{
    if (manager == null)
    {
        throw new ArgumentNullException(nameof(manager));
    }
    var token = await manager.CreateSecurityTokenAsync(user);
    var modifier = await GetUserModifierAsync(purpose, manager, user);

    return TotpService.GenerateCode(token, modifier).ToString("D6", CultureInfo.InvariantCulture);
}

ValidateAsync method:

public override async Task<bool> ValidateAsync(
    string purpose,
    string token,
    UserManager<ApplicationUser> manager,
    ApplicationUser user)
{
    if (manager == null)
    {
        throw new ArgumentNullException(nameof(manager));
    }
    int code;
    if (!int.TryParse(token, out code))
    {
        return false;
    }
    var securityToken = await manager.CreateSecurityTokenAsync(user);
    var modifier = await GetUserModifierAsync(purpose, manager, user);

    return securityToken != null && TotpService.ValidateCode(securityToken, code, modifier, _userOption.TwoFactorTotpExpiration);
}

Then, introduce the created class to Identity:

services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<IdentityContext>()
    .AddDefaultTokenProviders()
    .AddTokenProvider<CustomTotpSecurityStampBasedTokenProvider >("CustomTotp");

And when generating and validating the code, you should send its provider name to the UserManager:

var verificationCode = await _userManager.GenerateTwoFactorTokenAsync(user, "CustomTotp");
var verify = await _userManager.VerifyTwoFactorTokenAsync(user, "CustomTotp", request.VerificationCode);