Identity — Token Providers — Digitteck
Identity — Token Providers
dotnet·19 June 2020·3 min read

Identity — Token Providers

The Microsoft team provides a very configurable library for managing users and authentication. AspIdentity meets most demands for small and large applications alike, and its token provider system is one of the less-documented corners worth understanding.

Defining Token Provider Types

IdentityOptions lets you set the name of each token provider — but assigning the name does not register the provider. The names below come from TokenOptions static constants, though any string is valid as long as it matches the registered provider.

csharp
services.AddIdentityCore<ApplicationUser>(options =>
{
    options.Tokens.EmailConfirmationTokenProvider  = TokenOptions.DefaultEmailProvider;
    options.Tokens.PasswordResetTokenProvider      = TokenOptions.DefaultEmailProvider;
    options.Tokens.ChangeEmailTokenProvider        = TokenOptions.DefaultEmailProvider;
    options.Tokens.ChangePhoneNumberTokenProvider  = TokenOptions.DefaultPhoneProvider;
    options.Tokens.AuthenticatorTokenProvider      = TokenOptions.DefaultAuthenticatorProvider;
});

If you don't specify the provider for each type, TokenOptions falls back to DefaultProvider for most of them.

The static defaults are defined in the TokenOptions class:

csharp
public class TokenOptions
{
    public static readonly string DefaultProvider              = "Default";
    public static readonly string DefaultEmailProvider        = "Email";
    public static readonly string DefaultPhoneProvider        = "Phone";
    public static readonly string DefaultAuthenticatorProvider = "Authenticator";

    public string EmailConfirmationTokenProvider  { get; set; } = DefaultProvider;
    public string PasswordResetTokenProvider      { get; set; } = DefaultProvider;
    public string ChangeEmailTokenProvider        { get; set; } = DefaultProvider;
    public string ChangePhoneNumberTokenProvider  { get; set; } = DefaultPhoneProvider;
    public string AuthenticatorTokenProvider      { get; set; } = DefaultAuthenticatorProvider;
}

Register the Token Providers

The built-in extension method registers all four default implementations. It uses MakeGenericType at runtime to close the open generic over your user type:

csharp
// Built-in convenience extension
builder.AddDefaultTokenProviders();

// What it registers internally:
public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder)
{
    var userType = builder.UserType;
    return builder
        .AddTokenProvider(TokenOptions.DefaultProvider,
            typeof(DataProtectorTokenProvider<>).MakeGenericType(userType))
        .AddTokenProvider(TokenOptions.DefaultEmailProvider,
            typeof(EmailTokenProvider<>).MakeGenericType(userType))
        .AddTokenProvider(TokenOptions.DefaultPhoneProvider,
            typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType))
        .AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider,
            typeof(AuthenticatorTokenProvider<>).MakeGenericType(userType));
}

Alternatively, you can register them explicitly — which is clearer when you want to swap an individual provider:

csharp
// Equivalent explicit registration — clearer intent
builder
    .AddTokenProvider<DataProtectorTokenProvider<ApplicationUser>>(TokenOptions.DefaultProvider)
    .AddTokenProvider<EmailTokenProvider<ApplicationUser>>(TokenOptions.DefaultEmailProvider)
    .AddTokenProvider<PhoneNumberTokenProvider<ApplicationUser>>(TokenOptions.DefaultPhoneProvider)
    .AddTokenProvider<AuthenticatorTokenProvider<ApplicationUser>>(TokenOptions.DefaultAuthenticatorProvider);

How Each Provider Is Used

Each provider name maps to a specific pair of UserManager generate / validate methods. The provider is selected by name and then GenerateUserTokenAsync / VerifyUserTokenAsync delegate to it with a purpose string:

csharp
// 1. EmailConfirmationTokenProvider
//    Generate: UserManager.GenerateEmailConfirmationTokenAsync
//    Validate: UserManager.ConfirmEmailAsync
Task<string> GenerateEmailConfirmationTokenAsync(TUser user)
    => GenerateUserTokenAsync(user, Options.Tokens.EmailConfirmationTokenProvider, ConfirmEmailTokenPurpose);

// 2. PasswordResetTokenProvider
//    Generate: UserManager.GeneratePasswordResetTokenAsync
//    Validate: UserManager.ResetPasswordAsync
Task<string> GeneratePasswordResetTokenAsync(TUser user)
    => GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider, ResetPasswordTokenPurpose);

// 3. ChangeEmailTokenProvider
//    Generate: UserManager.GenerateChangeEmailTokenAsync
//    Validate: UserManager.ChangeEmailAsync
Task<string> GenerateChangeEmailTokenAsync(TUser user, string newEmail)
    => GenerateUserTokenAsync(user, Options.Tokens.ChangeEmailTokenProvider, GetChangeEmailTokenPurpose(newEmail));

// 4. ChangePhoneNumberTokenProvider
//    Generate: UserManager.GenerateChangePhoneNumberTokenAsync
//    Validate: UserManager.ChangePhoneNumberAsync
Task<string> GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber)
    => GenerateUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, GetChangePhoneTokenPurpose(phoneNumber));

// 5. AuthenticatorTokenProvider
//    Used internally by Identity for 2FA authenticator key verification
Task<bool> VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token)
    => VerifyUserTokenAsync(user, tokenProvider, TwoFactorTokenPurpose, token);

The AuthenticatorTokenProvider is more involved — it manages TOTP keys set up internally by Identity. It deserves its own article.

Tags

.NETASP.NET CoreIdentity
digitteck

© 2026 Digitteck