1
votes

I'm trying to add roles authentication in an existing ASP.NET (C#, MVC4) project I'm working on, using VS2013.

I had a similar problem a year ago with a different project, which I managed to solve, but now I've done the exact same thing and nothing happens: Seeding data and creating/managing roles in MVC4 - how hard can it be?

This is what I've done so far:

1. I have my own DataContext class that replaces the default UserContext class that comes with the scaffold:

public class DataContext : DbContext
{
    public DataContext() : base("DefaultConnection")
    {
    }

public DbSet<UserProfile> UserProfiles { get; set; }
}

2. In my initializer class I added this:

public class DataContextDbInitializer : DropCreateDatabaseIfModelChanges<DataContext>
{
protected override void Seed(DataContext context)
{
     if (!Roles.RoleExists("Admins"))
     {
                Roles.CreateRole("Admins");
     }
     if (!WebSecurity.UserExists("admin"))
     {
                WebSecurity.CreateUserAndAccount("admin", "123456");
     }
     if (!Roles.GetRolesForUser("admin").Contains("Admins"))
     {
                Roles.AddUsersToRoles(new[] { "admin" }, new[] { "Admins" });
     }
     base.Seed(context);
}

3. Added these lines to the Application_Start() method in Global.asax:

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
Database.SetInitializer(new DataContextDbInitializer());
DataContext c = new DataContext();
c.Database.Initialize(true);

4. Removed the [InitializeSimpleMembership] annotation from the AccountController, so I can initialize it from the very beginning of the application life cycle (using WebSecurity.InitializeDatabaseConnection in App_Start, as I've explained in number 3)

5. In Web.config I left authentication method as Forms (default) and my connection string looks like this:

  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-consultorio;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-consultorio.mdf" providerName="System.Data.SqlClient" />
  </connectionStrings>

Inside the system.web tag I added this:

<roleManager enabled="true" cacheRolesInCookie="true" />

To test if everything works, I added the [Authorize(Roles = "Admins")] annotation on top of a method that lists stuff from the DB in a controller.

  • If I run the application like this and click on the link that triggers the method with the [Authorize] annotation, I get a login screen. I enter valid credentials for user "admin" and the page is reloaded without throwing any errors (nothing like "wrong username"), exceptions or anything.
  • If I add WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); as the first line of my Seed(DataContext context) method in my initializer class (right before the "if" creating a role starts) I get the same behavior when trying to access the restricted methog (login screen is displayed and reloaded every time valid credentials are entered).
  • If I remove the WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); line from the Application_Start method in Global.asax then I get a System.InvalidOperationException when trying to log in: It stops when executing this: if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe)) inside the public ActionResult Login(LoginModel model, string returnUrl) method in the AccountController. The error message says "You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class. This call should be placed in an _AppStart.cshtml file in the root of your site." So I assume it doesn't like it when I remove that line from Global.asax. But I add the line again and then I'm back to the page reloading when trying to login.

What could the problem be?

1

1 Answers

1
votes

Well, I finally found the problem, so I'm posting here for posterity (maybe in a year I'll have the same problem and will find my own thread!) The code was fine. I just had the wrong database management method: since my database was already created (this is an existing project I was adding roles to), the Seed method was never called, thus leaving roles aside. I just had to switch to DropDatabaseAlways and voilá!