asp.net-mvc-4rolessimplemembership

Role based authentication in the new MVC 4 Internet template using simplemembership


I like the new simplemembership feature in MVC 4 internet template with links to OAuth for external logins in VS 2012 RTM. For the most part authentication feature are working. However even after spending over 8 hours on this I am unable to implement roles based authorization to work on my controllers. SimpleMembership is turning out to be anything but simple.

I have searched stackoverflow, googled and have read the latest by John Galloway, tried many suggestions and still have not been able to resovle this issue. It all started with getting Sql connection error and could not figure out why when the connection string and everything else was good. It took many hours to figure out the it is Roles class that is causing problem.

The [Authorize] attribute on controllers works as before for basic authentication. But any time I try to use Roles it give sql connection error (because it reverts to the old DefaultRolesProvider which tries to connect to default SqlExpress aspnetdb file and fails). So something like:

[Authorize(Roles="admin")]

does not work. It will work if I go back to the old asp.net membership providers, but then I lose the simple database tables, token bases confirmation and recovery, more secure password hashing and more importantly external logins via OAuth.

The only thing that works inside code and razor views is

User.IsInRole("admin")

which is OK for menu items and such, but ver cumbersome to implement inside every single Action in controller (and I do not like that it only tests for single role at a time).

I will greatly appreciate any guidance to resovle this issue.


Solution

  • Found an answer here by Mehdi Golchin which seems to take care of:

    [Authorize(Roles="admin,editor,publisher")]
    

    If I also add this to the home controller:

     [InitializeSimpleMembership]
    

    Because this attribute is on the Accounts controller, SimpleMembership database gets initialize only after the first use of the accounts controller like login/register. Even when the current user gets logged in from the cookie, the database is not initialized and so it throws an error. One solution is to put this attribute on the home controller which gets called when I launch my Website. But, then it needs to be placed on every controller because I check roles and display different menu items based on role.

    This is poor design as the database should be initialized on the App_Start and not when first used.

    I did try to put

    WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
    

    in the Global.asax Application_Start(), and it takes care of role checking in menu items using User.IsInRole("admin"), but then throw error in any controller with [Authorize(Roles="admin")] attribute, even with additional attribute [InitializeSimpleMembership] is applied.

    So right now the solution is to put `[InitializeSimpleMembership] on all controllers as a user may initially land on any page using external links.

    It still can't figure how to initialize the SimpleRolesProvider class to do more of the role management instead of just User.IsInRole().

    These things do work better in the webmatrix webpages site and obviously the port ot MVC is not complete. It conflicts and gets confused with the default asp.net membership providers.

    EDIT OK I was not thinking [InitializeSimpleMembership] filter could be applied globally by putting this line in the FilterConfig.cs in the App_Start folder:

    filters.Add(new InitializeSimpleMembershipAttribute());
    

    That takes care of that problem. Now need a solution for SimpleRolesProvider initialization or else I will have to write my own roles provider.

    UPDATE:

    This post by Scott Allen has solved all my problems.

    By including this in web.config:

    <roleManager enabled="true" defaultProvider="simple">
      <providers>
        <clear/>
        <add name="simple" type="WebMatrix.WebData.SimpleRoleProvider,               WebMatrix.WebData"/>
      </providers>      
    </roleManager>
    <membership defaultProvider="simple">
      <providers>
        <clear/>
        <add name="simple" type="WebMatrix.WebData.SimpleMembershipProvider,                          WebMatrix.WebData"/>
      </providers>
    </membership>
    

    all the methods of Roles and Membership classes become available and can be initialized in code as follows:

    var roles = (SimpleRoleProvider) Roles.Provider;
    var membership = (SimpleMembershipProvider) Membership.Provider;