EPiServer CMS UI AspNetIdentity OWIN authentication

Vote:
 

Trying to setup a blank EPiServer site with the AspNetIdentity authentication system. Following the instructions here: http://world.episerver.com/documentation/Items/Developers-Guide/Episerver-CMS/9/Security/episerver-aspnetidentity/, here http://world.episerver.com/documentation/Items/Developers-Guide/Episerver-CMS/9/Security/aspnet-identity-owin-authentication/ and also looking in the QuickSilbver template site it feels I have setup everything needed to run it.

I have installed the nuget package EPiServer.CMS.UI.AspNetIdentity, setup a SiteUser inheriting from IdentityUser and IUIUser, and created the Startup class.

I have also used the aspnet_identity.sql file from the QuickSilver solution to fill the database with some base users.

When accessing the /episerver url I am redirected to the /Util/Login.aspx as usual but when after logging in with a user I get the following error message not giving to much information:

Server Error in '/' Application.

Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[NullReferenceException: Object reference not set to an instance of an object.]
   EPiServer.UI.WebControls.Login.OnAuthenticate(AuthenticateEventArgs e) +100
   System.Web.UI.WebControls.Login.AttemptLogin() +145
   System.Web.UI.WebControls.Login.OnBubbleEvent(Object source, EventArgs e) +114
   System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +49
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +6015

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1069.1


Update:

Setting the app.AddCmsAspNetIdentity(new ApplicationOptions() { ConnectionStringName = "EPiServerDB" }); I get another error message

Server Error in '/' Application.

Invalid column name 'IsApproved'.
Invalid column name 'IsLockedOut'.
Invalid column name 'Comment'.
Invalid column name 'CreationDate'.
Invalid column name 'LastLoginDate'.
Invalid column name 'LastLockoutDate'.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'IsApproved'.
Invalid column name 'IsLockedOut'.
Invalid column name 'Comment'.
Invalid column name 'CreationDate'.
Invalid column name 'LastLoginDate'.
Invalid column name 'LastLockoutDate'.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[SqlException (0x80131904): Invalid column name 'IsApproved'.
Invalid column name 'IsLockedOut'.
Invalid column name 'Comment'.
Invalid column name 'CreationDate'.
Invalid column name 'LastLoginDate'.
Invalid column name 'LastLockoutDate'.]
   System.Data.SqlClient.<>c.b__167_0(Task`1 result) +1166158
   System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke() +108
   System.Threading.Tasks.Task.Execute() +71
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   System.Data.Entity.Core.EntityClient.Internal.d__c.MoveNext() +419

This is because the database is not updated with the properties from the EPiServer ApplicationUser. But to fix this I think I need to run migrations but trying the enable-migrations on the project from the Package Manager console only gives me the error message No context type was found in the assembly .

How do I properly initialie the database to be used with EPiServers AspNetIdentity?

#151628
Edited, Aug 01, 2016 14:36
Vote:
 

After some more trying I have got it working

I had to create a EPiServerDbContext inheriting from IdentityDbContext like this

public class EPiServerDbContext : IdentityDbContext<ApplicationUser>
    {
        public EPiServerDbContext() : base("EPiServerDB", throwIfV1Schema: false)
        {
        }

        public static EPiServerDbContext Create()
        {
            return new EPiServerDbContext();
        }
    }

After this I was able to do enable-migrations in the Package Manager Console.

Trying to do update-database told me about some pending changes so i ran Add-Migration UserUpdate and got a DBMigration which would create the tables needed for the User management.

After this I could run update-database and it created the tables. 

To add a user I then added the following to the Seed method in the Configuration class created by the enable-migrations command

context.Users.AddOrUpdate(u => u.Id,
                new EPiServer.Cms.UI.AspNetIdentity.ApplicationUser
                {
                    Id = "6008ecc6-a5a8-49dc-a47c-c1db614df77c",
                    CreationDate = DateTime.Now,
                    Email = "admin@site.se",
                    EmailConfirmed = true,
                    IsApproved = true,
                    IsLockedOut = false,
                    Username = "admin",
                    PasswordHash = "AAwsxpbbay95Ig5UUtJfqrz5QQZDWbbJShgza2BVP9sZAEaDvoC+UZ6HP1ER3b94FQ==",
                    SecurityStamp = "989acc4f-30bd-425d-9b20-7c7f85bee15b",
                    UserName = "admin",
                    TwoFactorEnabled = false,
                    LockoutEnabled = false,
                    AccessFailedCount = 0
                });

            context.Roles.AddOrUpdate(r => r.Name,
                new IdentityRole
                {
                    Name = "Administrators"
                },
                new IdentityRole
                {
                    Name = "WebAdmins"
                },
                new IdentityRole
                {
                    Name = "WebEditors"
                });
            context.SaveChanges();
            var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
            userManager.AddToRole("6008ecc6-a5a8-49dc-a47c-c1db614df77c", "Administrators");

That will create an admin user with the password store, connected to the Administrators role, that can be used to log in to the site with. Just run update-database after you have saved and built the project to create the user.

It feels like EPiServer needs to update their article describing this a bit...

#151636
Edited, Aug 01, 2016 17:03
Vote:
 

Thanks, this was helpful. It's a little crazy that you can specify a custom identity model in the startup with custom properties, but you need to create your own duplicated context to get migrations to be applied.

app.AddCmsAspNetIdentity<CustomUser>();

If anyone is interested in code-based migrations, you can specify a use Tobias' post above to make your own custom DB Context

public class EPiServerDbContext : IdentityDbContext<CustomUser>
    {
        public EPiServerDbContext() : base("EPiServerDB", throwIfV1Schema: false)
        {
        }

        public static EPiServerDbContext Create()
        {
            return new EPiServerDbContext();
        }
    }

After, you can use Nuget console to run

Enable-Migrations

Which will generate the following:

internal sealed class Configuration : DbMigrationsConfiguration<YourNamespace.EPiServerDbContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(YourNamespace.EPiServerDbContext context)
        {

        }
    }

All you need to do then is add a ContextKey matching what the site specifies in the _MigrationHistory SQL table. For the Social Alloy site, that's:

internal sealed class Configuration : DbMigrationsConfiguration<YourNamespace.EPiServerDbContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            ContextKey = "EPiServer.Cms.UI.AspNetIdentity.ApplicationDbContext`1[EPiServer.Cms.UI.AspNetIdentity.ApplicationUser]";
        }

        protected override void Seed(YourNamespace.EPiServerDbContext context)
        {

        }
    }

From there, you can add properties to your custom IdentityUser (mine is CustomUser) and run

Add-Migration "<MigrationName>"
Update-Database

Enjoy.

#177894
Edited, Apr 23, 2017 6:02
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.