HomeDev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

EPiServer.CMS.UI.AspNetIdentity OWIN authentication

Describes the authentication module for managing users and roles.

You can configure the application to use AspNetIdentity as the authentication module for managing users and roles. This configuration requires the following NuGet package as a dependency: EPiServer.CMS.UI.AspNetIdentity.

To use and configure AspNetIdentity OWIN-based authentication:

  1. Set the authentication mode in the system.web section of the web.config file as shown:

    <authentication mode="None"></authentication>
    
  2. Clear the membership and rolemanager providers from web.config as shown:

    <membership>
      <providers>
        <clear />
      </providers>
    </membership>
            
    <roleManager enabled="false">
      <providers>
        <clear />
      </providers>
    </roleManager>
    
  3. Because OWIN pipeline is a startup class needed to configure the application, add the following code to the startup class:

    using EPiServer.Cms.UI.AspNetIdentity;
    using Microsoft.AspNet.Identity;
    using Microsoft.AspNet.Identity.Owin;
    using Microsoft.Owin;
    using Microsoft.Owin.Security.Cookies;
    using Owin;
    using System; 
    
    [assembly: OwinStartup(typeof(Startup))]
    
    public void Configuration(IAppBuilder app)
    {
       // Add CMS integration for ASP.NET Identity
       app.AddCmsAspNetIdentity<ApplicationUser>();   
       // Use cookie authentication
       app.UseCookieAuthentication(new CookieAuthenticationOptions
       {
         AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
         LoginPath = new PathString(login-path),
         Provider = new CookieAuthenticationProvider
         {
           OnValidateIdentity = 
             SecurityStampValidator.OnValidateIdentity<ApplicationUserManager<ApplicationUser>,ApplicationUser>(
               validateInterval: TimeSpan.FromMinutes(30),
               regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
         }
       });
    }
    

The EPiServer.CMS.UI.AspNetIdentity NuGet package implements the UIUsersManager, UIRoleManager, SecurityEntityProvider and SignInManager providers, which need to be integrated with the Optimizely user interface. This means the users, roles and access rights can be managed from admin view. And, the Optimizely user interface login page (/util/login.aspx) can be used for login.

Custom user database

By default, the ApplicationContext uses the EPiServerDB as a connection string name to save AspNet Users and roles. You can override it like this: 

app.AddCmsAspNetIdentity<ApplicationUser>(new ApplicationOptions() 
  { ConnectionStringName = " connection string name" });

Custom user model

There are two ways to define a custom user model.

  • Inherit from EPiServer.Cms.UI.AspNetIdentity.ApplicationUser, like this:
    public class CustomUser : ApplicationUser
            {
               //your custom properites
            }
    
  • Inherit from Microsoft.AspNet.Identity.EntityFramework.IdentityUser and the EPiServer.Shell.Security.IUIUser interfaces, like this:
    public class CustomUser : IdentityUser, IUIUser
                {
                    public string Comment { get; set; }
                    public bool IsApproved { get; set; }
                    public bool IsLockedOut { get; set; }
            
                    [Column(TypeName = "datetime2")]
                    public DateTime CreationDate { get; set; }
                    
                    [Column(TypeName = "datetime2")]
                    public DateTime? LastLockoutDate { get; set; }
                    
                    [Column(TypeName = "datetime2")]
                    public DateTime? LastLoginDate { get; set; }
            
                    public string PasswordQuestion{get;}
            
                    public string ProviderName
                    {
                        get { return "MyProviderName"; }
                    }
            
                    [NotMapped]
                    public string Username 
                    { 
                        get { return base.UserName; } 
                        set { base.UserName = value; }
                    }
                }
    

After defining a custom user model, you need to configure it in the OWIN startup class, like this:

public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Add CMS integration for ASP.NET Identity             
                app.AddCmsAspNetIdentity<CustomUser>();
    
                // Use cookie authentication
                app.UseCookieAuthentication(new CookieAuthenticationOptions
                {
                    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                    LoginPath = new PathString(YourLoginPath or "/Util/Login.aspx"),
                    Provider = new CookieAuthenticationProvider
                    {
                     OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager<CustomUser>, CustomUser>(
                       validateInterval: TimeSpan.FromMinutes(30),
                       regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
                    }
                });
            }
        }

SecurityEntityProvider 

The EPiServer.CMS.UI.AspNetIdentity implements and registers the UIUserProvider, UIRoleProvider, UISignInManager and SecurityEntity provider in the container. To override them, you need to programmatically register it in the InitializeModule, like this:

[EPiServer.Framework.InitializableModule]
        [EPiServer.Framework.ModuleDependency(typeof(EPiServer.Cms.UI.AspNetIdentity.ApplicationSecurityEntityInitialization))]
        [EPiServer.Framework.ModuleDependency(typeof(EPiServerUIInitialization))]
        public class MyInitializeModule : EPiServer.ServiceLocation.IConfigurableModule
        {
            public void ConfigureContainer(EPiServer.ServiceLocation.ServiceConfigurationContext context)
            {
               //Configure your providers
            }
            public void Initialize(EPiServer.Framework.Initialization.InitializationEngine context) { }
            public void Uninitialize(EPiServer.Framework.Initialization.InitializationEngine context) { }

Extend the Cms Asp.net Identity implementation

The Asp.net Identity system is using the Owin pipeline which makes it a bit harder to replace our default implementation, it is not just a matter of registering a new class in the IoC container.

Instead, you need to take control of the pipeline setup. We are exposing the default create delegates for all standard Asp.net Identity classes, which means that you need to create your own Pipeline initialization method and call the create delegates in the correct order. This is an example on how you can do that in your own IAppBuilder extension method.

public static class ApplicationBuilderExtensions
    {
        public static IAppBuilder SetupCustomAspNetIdentity<TUser>(this IAppBuilder app) where TUser : IdentityUser, IUIUser, new()
        {
            var applicationOptions = new ApplicationOptions
            {
                DataProtectionProvider = app.GetDataProtectionProvider()
            };
    
            // Configure the db context, user manager and signin manager to use a single instance per request by using
            // the default create delegates
            app.CreatePerOwinContext<ApplicationOptions>(() => applicationOptions);
            app.CreatePerOwinContext<ApplicationDbContext<TUser>>(ApplicationDbContext<TUser>.Create);
            app.CreatePerOwinContext<ApplicationRoleManager<TUser>>(ApplicationRoleManager<TUser>.Create);
            app.CreatePerOwinContext<ApplicationUserManager<TUser>>(ApplicationUserManager<TUser>.Create);
            app.CreatePerOwinContext<ApplicationSignInManager<TUser>>(ApplicationSignInManager<TUser>.Create);
    
            // Configure the application 
            app.CreatePerOwinContext<UIUserProvider>(ApplicationUserProvider<TUser>.Create);
            app.CreatePerOwinContext<UIRoleProvider>(ApplicationRoleProvider<TUser>.Create);
            app.CreatePerOwinContext<UIUserManager>(ApplicationUIUserManager<TUser>.Create);
            app.CreatePerOwinContext<UISignInManager>(ApplicationUISignInManager<TUser>.Create);
    
            // Saving the connection string in the case dbcontext be requested from none web context
            ConnectionStringNameResolver.ConnectionStringNameFromOptions = applicationOptions.ConnectionStringName;
            
            return app; 
        }
    }

When you have your new setup method you need to call that method in the Startup instead of the built-in one.

public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ...
    
            // Add CMS integration for ASP.NET Identity
            app.SetupCustomAspNetIdentity<ApplicationUser>();
    
            ...
        }
    }

When you have your custom pipeline running, it is possible to change things like the default PasswordHasher, the default PasswordValidator, etc.

The first thing you need to do is to create a new create delegate for the ApplicationUserManager and change the relevant values.

public static class ApplicationBuilderExtensions
    {
        public static ApplicationUserManager<TUser> CreateApplicationUserManager<TUser>(IdentityFactoryOptions<ApplicationUserManager<TUser>> options, IOwinContext context) where TUser : IdentityUser, IUIUser, new()
        {
            var manager = new ApplicationUserManager<TUser>(new UserStore<TUser>(context.Get<ApplicationDbContext<TUser>>()));
    
            // Configure validation logic for usernames
            manager.UserValidator = new UserValidator<TUser>(manager)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };
    
            // Change Password hasher
            manager.PasswordHasher = new SqlPasswordHasher();
    
            // Configure validation logic for passwords
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = true,
                RequireDigit = true,
                RequireLowercase = true,
                RequireUppercase = true
            };
    
            // Configure user lockout defaults
            manager.UserLockoutEnabledByDefault = true;
            manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
            manager.MaxFailedAccessAttemptsBeforeLockout = 5;
    
            var provider = context.Get<ApplicationOptions>().DataProtectionProvider.Create("EPiServerAspNetIdentity");
            manager.UserTokenProvider = new DataProtectorTokenProvider<TUser>(provider);
    
            return manager;
        }
    }

When you have your own create delegate you have to replace the default create delegate in the SetupCustomAspNetIdentity method.

Change

app.CreatePerOwinContext<ApplicationUserManager<TUser>>(ApplicationUserManager<TUser>.Create);

to

app.CreatePerOwinContext<ApplicationUserManager<TUser>>(CreateApplicationUserManager);