Search code examples
c#asp.net-mvcasp.net-mvc-4startupasp.net-identity-2

Where is the appropriate spot to seed and login a default user with ASP.NET MVC Identity 2.0?


I have an application that uses Identity 2.0 with Owin.

I want the application to seed a default user and log in automatically while on development (I'm wrapping the method and its call with #if DEBUG condition).

I tried adding the following method to IdentityConfig.csApplicationUserManager.Create method , but after calling it, HttpContext.Current.User is still null at the next call.

private void SeedAndLoginDefaultUser()
{
  var user = this.FindByName("***");
  if (user == null)
    this.Create(new ApplicationUser { /* ... */ }, "***");

  using (var signInManager = new ApplicationSignInManager(this, 
      HttpContext.Current.GetOwinContext().Authentication))
    signInManager.PasswordSignIn(user.UserName, "***", true, false);
}

But it keeps on authenticating the user over and over. I'm sure there should be a more appropriate location to perform this automation.
P.S. doing it from the Global.asax.cs or Startup.cs is impossible, since owin isn't initialized yet at that stage.

I tried adding it in the the Context initializer, the user gets created, but the login fails.

Update

on this answer:

Actually the UserManager is anyway overridden in the IdentityConfig.cs class. But what I'm thinking is, your solution will check for user existence each time a method attributed under CustomAuthorize is called. Whereas I only want to perform the default user creation and login only at app startup.

Update 2

In my database initializer I have some crucial seed tables, that has data related to user, which means, I have to create the user in the seed method. So my question is now, when I create a user manually as the following code:

public class Initializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
  private static volatile bool processing;
  protected override void Seed(ApplicationDbContext context)
  {
    if (processing)
      return;
    processing = true;
    using (var um = ApplicationUserManager.Get())
    {
      var shimmy = new ApplicationUser
       {
         //Id = Guid.NewGuid().ToString(),
         FirstName = "Shimmy",
         LastName = "Weitzhandler",
         UserName = "***",
         Email = "***",
         //EmailConfirmed = true,
       };
      var task = um.CreateAsync(shimmy, "***");
      task.Wait();
      var result = task.Result;

      if (result.Succeeded)
      {
        using (var sm = ApplicationSignInManager.Get())
        {
          var siTask = sm.SignInAsync(shimmy, true, true);
          siTask.Wait();
          if (siTask.Exception != null)
          {
            throw siTask.Exception;
          }
        }
      }
      else
      {
        throw new InvalidOperationException();
      }
    }

    //seed my tables
    processing = false;
  }
}

As you can see I even added a static member to avoid looping back the initializer, but I believe it still doesn't help. Because when I'm trying to create the user manually (adding to the Users table, using the UserManager.PasswordHasher, I then can't log in to the account. But the problem with the code above is that I'm calling it from the Seed, and I suspect it calls itself back internally when I try to create a user, then it gets stuck. The resolution I'm looking for is how to create a user manually and be able to login to that account. I'm probably missing some point in the password hashing / user verification / whatever else.


Solution

  • You need to this before the default Autorization starts. To do this the easiest way is to create your own AuthorizeAttribute.

    public class MyCustomAuthorizeAttribute: AuthorizeAttribute
    {
       protected override bool AuthorizeCore(HttpContextBase httpContext)
       {
          #if DEBUG
            return true;
          #else
    
          return base.AuthorizeCore(httpContext);
       }
    
       public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
       {
          #if DEBUG
          // CHANGE TO YOUR USER MANAGER
          var userManger = filterContext.HttpContext.GetOwinContext().GetUserManager<ApplicationUser>();
    
          var user = userManger.FindByName("***");
          if (user == null)
             this.Create(new ApplicationUser {/* ... */}, "***");
    
          using(var signInManager = new ApplicationSignInManager(userManger, filterContext.HttpContext.GetOwinContext().Authentication))
             signInManager.PasswordSignIn(user.UserName, "***", true, false);
    
          return;
          #endif
    
          return base.OnAuthorization(filterContext);
       }
    }
    

    And then replace all existing Authorize attributes with your own, i.e.

    [MyCustomAuthorize]
    
    [MyCustomAuthorize(Roles = "ROLENAME")]
    

    Solution 2:

    If don't want do signin for each request, you can skip that but you still need to fetch the user from DB. Implement your service to getUser. Otherwise you can just hard code a user with a name and a role (if necessary).

    public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
    {
        #if DEBUG
        // CHANGE TO YOUR USER MANAGER
        var user = GETUSER("");
        var principal = new GenericPrincipal(new GenericIdentity(user.Name), new[] { user.RoleName });
    
        Thread.CurrentPrincipal = principal;
        HttpContext.Current.User = principal;
    
        return;
        #endif
    
        return base.OnAuthorization(filterContext);
    }