ASP.NET Core 3.1 - 2FA Cookie Schemes


Ken Haggerty
Created 08/20/2020 - Updated 08/31/2020 06:28

This article will demonstrate the implementation of multiple cookie schemes like ASP.NET Core Identity. 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.

Users Without Identity Project and Article Series

Access to the research project source code may be purchased on KenHaggerty.Com at Manage > Assets. A project which implements users without Identity has been published to demo.kenhaggerty.com. I enjoy writing these articles. It often enhances and clarifies my coding. The research project is a result of a lot of refactoring and hopefully provides logical segues for the articles. Thank you for supporting my efforts.

The UWIP used the single CookieAuthenticationDefaults. AuthenticationScheme before I implemented 2FA cookie schemes like ASP.NET Core Identity. The UWIP now employs the multiple cookie schemes with ApplicationScheme and 2 additional 2FA schemes TwoFactorUserIdScheme, and TwoFactorRememberMeScheme. I developed a UWIPConstants class to represent the cookie scheme and claim names.

Entities > UWIPConstants.cs:
//
// Summary:
//     Represents values used to configure the cookies and claims.
public class UWIPConstants
{
    //
    // Summary:
    //     The scheme used to identify application authentication cookies.
    public static readonly string ApplicationScheme = "UWIP.ApplicationScheme";
    //
    // Summary:
    //     The scheme used to identify Two Factor authentication cookies for saving the Remember Me state.
    public static readonly string TwoFactorRememberMeScheme = "UWIP.TwoFactorRememberMeScheme";
    //
    // Summary:
    //     The scheme used to identify Two Factor authentication cookies for round tripping user identities.
    public static readonly string TwoFactorUserIdScheme = "UWIP.TwoFactorUserIdScheme";
    //
    // Summary:
    //     The claim type used to hold the user's TwoFactorEnabled state.
    public static readonly string TwoFactorEnabledClaimType = "UWIP.TwoFactorEnabledClaimType";
    //
    // Summary:
    //     The claim type used to redirect a user to the change password page.
    public static readonly string MustChangePasswordClaimType = "UWIP.MustChangePasswordClaimType";
    //
    // Summary:
    //     The value used to identify the source of the Two Factor authentication key.
    public static readonly string TwoFactorLoginProvider = "AppUserTokens";
    //
    // Summary:
    //     The value used to identify the MultiFactor authentication method.
    public static readonly string MultiFactorAuthentication = "MultiFactor";
    //
    // Summary:
    //     The value used to identify the Password only authentication method.
    public static readonly string PasswordAuthentication = "Password";
    //
    // Summary:
    //     The value used to identify the Admins policy.
    public static readonly string AdminsPolicy = "Admins";
    //
    // Summary:
    //     The value used to identify the Admin role.
    public static readonly string AdminRole = "Admin";        

    public UWIPConstants() { }
}

The CookieAuthenticationDefaults .AuthenticationScheme provides default paths for Login, Logout, and AccessDenied.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.Events = new CookieAuthenticationEvents
        {
            OnValidatePrincipal = CookieValidator.ValidateAsync
        };
    });

With multiple schemes, you configure the options for each scheme and declare the default scheme with the AddAuthentication parameter.

Edit Startup.cs > ConfigureServices:
services.AddAuthentication(UWIPConstants.ApplicationScheme)
    .AddCookie(UWIPConstants.ApplicationScheme, options =>
    {
        options.LoginPath = "/Account/Login";
        options.LogoutPath = "/Account/Logout";
        options.AccessDeniedPath = "/Account/AccessDenied";
        options.SlidingExpiration = true;
        options.Events = new CookieAuthenticationEvents
        {
            OnValidatePrincipal = CookieValidator.ValidateAsync
        };
    })
    .AddCookie(UWIPConstants.TwoFactorUserIdScheme, options =>
    {
        options.Cookie.Name = UWIPConstants.TwoFactorUserIdScheme;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
    })
    .AddCookie(UWIPConstants.TwoFactorRememberMeScheme, options =>
    {
        options.Cookie.Name = UWIPConstants.TwoFactorRememberMeScheme;
    });

The ApplicationScheme cookie is applied when the user is authenticated. The TwoFactorUserIdScheme cookie is applied when a user who has 2FA enabled, successfully signs in with a username and password. The TwoFactorUserIdScheme cookie has a 5 minute ExpireTimeSpan and is used to transfer the user id from the log in page and the 2FA log in page.

The TwoFactorRememberMeScheme cookie is used to optionally remember the browser used with 2FA authentication. This cookie will persist on the browser after the user signs out. Future sign ins by the same user on the same browser will authenticate the user without 2FA verification until the browser is forgotten.

2FA Remember Machine
2FA Forget Browser
Update 08/28/2020

I updated the series article links.

Update 08/31/2020

I added the 2FA Admin Override article link.

Article Tags:

2FA Authorization

Comment Count = 0

Please log in to comment or follow.

Login Register
Follow to get web notifications when new comments are posted to this article.
Logged in users receive web notifications for new articles, topics and assets.
Web Notifications