Authentication, Identity, and OWIN

This section documents ASP.NET MVC 5.3.x, ASP.NET Web API 2.2, ASP.NET Web Pages 3, OWIN/Katana, SignalR 2, and ASP.NET Identity 2 — all running on .NET Framework 4.8.1 — the System.Web-hosted MVC framework, its routing, Razor views, HTML helpers, model binding, filters, and the OWIN-based authentication/Identity stack — as described by the official documentation at Microsoft Learn (plus Web API, Web Pages, SignalR, and Identity), which are the reference these pages are written and verified against.

This is not ASP.NET Core MVC. ASP.NET MVC 5 is the pre-Core, System.Web-hosted MVC framework; it is functionally frozen and receives only security fixes. For the current, cross-platform MVC framework see MVC Controllers and Views under ASP.NET Core (Blazor).

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page documents OWIN/Katana and ASP.NET Identity 2.x on .NET Framework 4.8.1 — not ASP.NET Core Identity, a rewritten library with a different API surface (see Authentication and ASP.NET Core Identity under ASP.NET Core).

MVC 5’s template moved away from the older Forms Authentication module (<authentication mode="Forms"> in web.config, FormsAuthentication.SetAuthCookie) to OWIN cookie authentication (Microsoft.Owin.Security.Cookies), which issues and validates the auth cookie through OWIN middleware instead of a System.Web HttpModule. This is what let ASP.NET Identity 2 support claims-based principals (ClaimsPrincipal) and external logins through the same middleware pipeline used for local accounts, rather than bolting them onto Forms Authentication’s simple username-only cookie.

The OWIN/Katana pipeline, Startup.cs, IAppBuilder

Katana is Microsoft’s OWIN implementation for System.Web/IIS; [assembly: OwinStartup] tells it which class’s Configuration(IAppBuilder) method to run at startup, independently of Global.asax’s `Application_Start:

// App_Start/Startup.Auth.cs (a partial class; the other half is App_Start/Startup.cs)
[assembly: OwinStartup(typeof(MyApp.Startup))]
namespace MyApp
{
    public partial class Startup
    {
        public void ConfigureAuth(IAppBuilder app)
        {
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = SecurityStampValidator
                        .OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                            TimeSpan.FromMinutes(30),
                            (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });

            app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
            {
                ClientId = "...", ClientSecret = "..."
            });
        }
    }
}

IAppBuilder.Use/UseCookieAuthentication/etc. build a middleware chain conceptually identical to ASP.NET Core’s app.Use(…​) (see Request Pipeline and Middleware under ASP.NET Core) but predating it — OWIN was the middleware model .NET Framework had before ASP.NET Core existed, and Web API 2’s OWIN self-host (see ASP.NET Web API 2) and SignalR 2 (see SignalR 2) both build on this same pipeline.

ASP.NET Identity 2.x

Type Role

IdentityUser / ApplicationUser : IdentityUser

The user entity — extend with app-specific properties.

IdentityRole

The role entity.

UserManager<TUser>

Create/find/update users, hash and verify passwords, manage claims/roles/tokens.

RoleManager<TRole>

Create/find/delete roles.

SignInManager<TUser, TKey>

Password sign-in, two-factor sign-in, and external-login sign-in, wired to the OWIN cookie middleware above.

IdentityDbContext<TUser>

An EF6 DbContext (see Data Access with EF6) providing the default Identity schema (AspNetUsers, AspNetRoles, AspNetUserClaims, …​).

IUserStore<TUser>

The persistence abstraction UserManager delegates to — the default EF6 implementation can be replaced (e.g. with a MongoDB- or Dapper-backed store) without changing UserManager call sites.

public class ApplicationUser : IdentityUser
{
    public string DisplayName { get; set; }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext() : base("DefaultConnection") { }
    public static ApplicationDbContext Create() => new ApplicationDbContext();
}
var result = await UserManager.CreateAsync(
    new ApplicationUser { UserName = model.Email, Email = model.Email }, model.Password);

if (result.Succeeded)
{
    var user = await UserManager.FindByNameAsync(model.Email);
    var confirmToken = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
    // email the confirmation link containing confirmToken
    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}

Password hashing (PBKDF2 by default via IPasswordHasher), password/user-name validators (IPasswordValidator, IUserValidator, configured in App_Start/IdentityConfig.cs), email/phone confirmation, two-factor authentication (GenerateTwoFactorTokenAsync), account lockout (MaxFailedAccessAttemptsBeforeLockout), and claims (UserManager.AddClaimAsync) are all UserManager/SignInManager responsibilities configured once in IdentityConfig.cs:

// App_Start/IdentityConfig.cs
public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public static ApplicationUserManager Create(
        IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
        manager.PasswordValidator = new PasswordValidator { RequiredLength = 8, RequireNonLetterOrDigit = true };
        manager.UserLockoutEnabledByDefault = true;
        manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(15);
        manager.MaxFailedAccessAttemptsBeforeLockout = 5;
        return manager;
    }
}

External logins

Google, Facebook, Microsoft, and Twitter providers register via app.Use*Authentication(…​) in Startup.Auth.cs (see the Google example above); the callback flow lands on Account/ExternalLoginCallback, which either signs in an existing linked account or prompts the user to complete registration:

[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
    if (loginInfo == null) return RedirectToAction("Login");

    var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
    return result switch
    {
        SignInStatus.Success => RedirectToLocal(returnUrl),
        SignInStatus.LockedOut => View("Lockout"),
        _ => View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email })
    };
}

[Authorize(Roles = …​)]

[Authorize(Roles = "Administrator,Support")]
public class AdminController : Controller { ... }

Role membership is stored via IdentityUserRole/AspNetUserRoles, and checked the same way any System.Web.Mvc.AuthorizeAttribute checks roles — see Filters for how [Authorize] fits as an authorization filter.

Migrating from Membership to Identity

The legacy ASP.NET Membership provider (Membership.CreateUser, aspnet_Membership/aspnet_Users SQL tables, Roles.AddUserToRole) predates OWIN entirely and is not compatible with Identity’s schema or password hash format. A migration typically: creates the Identity schema (AspNetUsers, etc.) alongside the old Membership tables, writes a one-time script mapping aspnet_Users/aspnet_Membership rows into AspNetUsers (re-hashing passwords is not possible without the plaintext, so migrated users are commonly forced through a password-reset flow on first login), and migrates aspnet_Roles/aspnet_UsersInRoles into AspNetRoles/ AspNetUserRoles. See Migrating an Existing Website from SQL Membership to ASP.NET Identity.

Next: Security Hardening builds on this authentication foundation with CSRF, XSS, and the rest of the OWASP Top 10.