ASP.NET Core 3.1 - 2FA User Tokens

Ken Haggerty
Created 08/20/2020 - Updated 11/25/2020 00:22

This article will describe the implementation of a TwoFactorEnabled property for the user and a related AppUserToken entity/table. I will assume you have created a new ASP.NET Core 3.1 Razor Pages project. See Tutorial: Get started with Razor Pages in ASP.NET Core. You should review the earlier articles of the Users Without Identity Project series.

The Users Without Identity Project (UWIP) implements AppUser, a minimal entity which is created by administrators who invite the user to login. To implement 2FA, we need a TwoFactorEnabled property for the user and a related AppUserToken entity.

Add the TwoFactorEnabled property and the virtual AppUserTokens navigational property to AppUser.

Edit Entities > AppUser.cs:
public class AppUser
    public int Id { get; set; }
    [Display(Name = "Login Name")]
    [StringLength(32, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
    public string LoginName { get; set; }
    public string LoginNameUppercase { get; set; }
    [MaxLength(84, ErrorMessage = "The {0} is max {1} characters long.")]
    public string PasswordHash { get; set; }
    [Display(Name = "MCP")]
    public bool MustChangePassword { get; set; }
    [Display(Name = "2FA")]
    public bool TwoFactorEnabled { get; set; }
    [Display(Name = "Admin")]
    public bool IsAdmin { get; set; }
    public byte[] RowVersion { get; set; }
    public virtual ICollection<AppUserToken> AppUserTokens { get; set; }

Add the AppUserToken entity with the virtual AppUser navigational property.

Entities > AppUserToken.cs:
public class AppUserToken
    public int AppUserId { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
    public virtual AppUser AppUser { get; set; }

Add the AppUserTokens property to ApplicationDbContext and the composite primary key to the OnModelCreating method.

Edit Data > ApplicationDbContext.cs:
public class ApplicationDbContext : DbContext
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)

    public DbSet<AppUser> AppUsers { get; set; }
    public DbSet<AppUserToken> AppUserTokens { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)

        // Set a unique constraint for LoginNameUppercase
            .HasIndex(u => u.LoginNameUppercase)

        // Composite primary key
            .HasKey(t => new { t.AppUserId, t.Name })

Use the Package Manager Console to add an InitialAppUserTokens migration and update the database. The navigational properties will create a ForeignKey with a cascade delete rule.

public partial class InitialAppUserTokens : Migration
    protected override void Up(MigrationBuilder migrationBuilder)
            name: "TwoFactorEnabled",
            table: "AppUsers",
            nullable: false,
            defaultValue: false);

            name: "AppUserTokens",
            columns: table => new
                AppUserId = table.Column<int>(nullable: false),
                Name = table.Column<string>(maxLength: 128, nullable: false),
                Value = table.Column<string>(nullable: true)
            constraints: table =>
                table.PrimaryKey("PK_AppUserTokens", x => new { x.AppUserId, x.Name })
                    .Annotation("SqlServer:Clustered", false);
                    name: "FK_AppUserTokens_AppUsers_AppUserId",
                    column: x => x.AppUserId,
                    principalTable: "AppUsers",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);

    protected override void Down(MigrationBuilder migrationBuilder)
            name: "AppUserTokens");

            name: "TwoFactorEnabled",
            table: "AppUsers");
Update 11/24/2020

I added the Rename Related Entities article and updated the project and article links. Updated code formatting.

Article Tags:

2FA Authorization EF Core Model

Comment Count = 0

Please log in to comment.

Login Register
Logged in users receive web notifications.
Web Notifications