Implementing custom Membership Provider and Role Provider for Authenticating ASP.NET MVC Applications

14. October 2009 06:58 by Matt Wrock in   //  Tags:   //   Comments (35)
DotNetShoutout
DotnetKicks

The .NET framework provides a provider model that allows developers to implement common user management and authentication functionality in ASP.NET applications. Out of the box, the framework ships with a few providers that allow you to easily wire up a user management system with very little to zero back end code. For instance, you can use the SqlMembershipProvider and the SqlRoleProvider which will create a database for you with tables for maintaining users and their roles. There are also providers available that use Active Directory as the user and role data store.

This is great in many cases and can save you from writing a lot of tedious plumbing code. However, there are many instances where this may not be appropriate:

  • I personally don’t feel comfortable having third party code create my user schema. Perhaps I’m being overly sensitive here.
  • You don’t use Sql Server or Active Directory. If you are using MySql to manage your user data, neither the out of the box providers will work for you.
  • You already have your own user data and schema which you want to continue using. You will need to implement your own providers to work with your schema.

I fall into the first and third scenarios listed above. I have multiple applications that all run on top of a common user/role management database. I want to be able to use Forms Authentication to provide my web security and I want all user validation and role checking to leverage the database schema that I already have. Fortunately, creating custom Membership and Role providers proved to be rather easy and this post will walk you through the necessary steps to get up and running.

Defining Users, Roles and Rights

My schema has the following key tables:
A users table which defines individual users and their user names, human names, passwords and role.

  • A role table that defines individual roles. It’s a simple lookup table with role_id and role_name columns.
  • A right table which is also a simple lookup table with Right_id and right_name columns.
  • A role_right table which defines many to many relationships between roles and rights. It has a role_id and right_id columns. Individual roles contain a collection of one to N number of rights.

These tables are mapped to classes via nHibernate.

Here is the User class:

    public class User : IPrincipal
    {
        protected User() { }
        public User(int userId, string userName, string fullName, string password)
        {
            UserId = userId;
            UserName = userName;
            FullName = fullName;
            Password = password;
        }
        public virtual int UserId { get; set; }
        public virtual string UserName { get; set; }
        public virtual string FullName { get; set; }
        public virtual string Password { get; set; }
        public virtual IIdentity Identity
        {
            get;
            set;
        }
        public virtual bool IsInRole(string role)
        {
            if (Role.Description.ToLower() == role.ToLower())
                return true;
            foreach (Right right in Role.Rights)
            {
                if (right.Description.ToLower() == role.ToLower())
                    return true;
            }
            return false;
        }
    }

 

You will notice that User derives from IPrincipal. This is not necessary to allow User to operate with my MembershipProvider implementation, but it allows me the option to use the User class within other frameworks that work with IPrincipal. For example, I may want to be able to directly interact with User when calling the Controller base class User property. This just requires me to implement the Identity property and the bool IsInRole(string roleName) method. When we get to the RoleProvider implementation, you will see that the IsInRole implementation is used there. You may also notice something else that may appear peculiar: a role here can be either a role or a right. This might be a bit of a hack to shim my finer grained right based schema into the ASP.NET role based framework, but it works for me.

Here are the Role and Right classes. They are simple data objects:

   public  class Role
   {
         protected Role() { }
        public Role(int roleid, string roleDescription)
        {
            RoleId = roleid;
            Description = roleDescription;
        }
        public virtual int RoleId { get; set; }
        public virtual string Description { get; set; }
        public virtual IList<Right> Rights { get; set; }
   }
    public class Right
    {
        protected Right() { }
        public Right(int rightId, string description)
        {
            RightId = rightId;
            Description = description;      
        }
        public virtual int RightId { get; set; }
        public virtual string Description { get; set; }
    }

 

Implementing the providers

So now with the basic data classes behind us, we can implement the membership and role providers. These implementations must derrive from MembershipProvider and RoleProvider. Note that these base classes contain a lot of methods that I have no use for. The Membership Provider model was designed to handle all sorts of user related functionality like creating users, modifying paswords, etc. I just need basic logon and role checking; so a lot of my methods throw NotImplementedExceptions. However, all methods required to handle authentication and role checking are implemented.


Here is the Membership Provider:

    public class AdminMemberProvider : MembershipProvider
    {
        #region Unimplemented MembershipProvider Methods
        public override string ApplicationName
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            throw new NotImplementedException();
        }
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new NotImplementedException();
        }
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            throw new NotImplementedException();
        }
      
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            throw new NotImplementedException();
        }
        public override bool EnablePasswordReset
        {
            get { throw new NotImplementedException(); }
        }
        public override bool EnablePasswordRetrieval
        {
            get { throw new NotImplementedException(); }
        }
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }
        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }
        public override int GetNumberOfUsersOnline()
        {
            throw new NotImplementedException();
        }
        public override string GetPassword(string username, string answer)
        {
            throw new NotImplementedException();
        }
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            throw new NotImplementedException();
        }
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            throw new NotImplementedException();
        }
        public override string GetUserNameByEmail(string email)
        {
            throw new NotImplementedException();
        }
        public override int MaxInvalidPasswordAttempts
        {
            get { throw new NotImplementedException(); }
        }
        public override int MinRequiredNonAlphanumericCharacters
        {
            get { throw new NotImplementedException(); }
        }
        public override int MinRequiredPasswordLength
        {
            get { throw new NotImplementedException(); }
        }
        public override int PasswordAttemptWindow
        {
            get { throw new NotImplementedException(); }
        }
        public override MembershipPasswordFormat PasswordFormat
        {
            get { throw new NotImplementedException(); }
        }
        public override string PasswordStrengthRegularExpression
        {
            get { throw new NotImplementedException(); }
        }
        public override bool RequiresQuestionAndAnswer
        {
            get { throw new NotImplementedException(); }
        }
        public override bool RequiresUniqueEmail
        {
            get { throw new NotImplementedException(); }
        }
        public override string ResetPassword(string username, string answer)
        {
            throw new NotImplementedException();
        }
        public override bool UnlockUser(string userName)
        {
            throw new NotImplementedException();
        }
        public override void UpdateUser(MembershipUser user)
        {
            throw new NotImplementedException();
        }
       
        #endregion
        IUserRepository _repository;
        public AdminMemberProvider() : this(null)
        {
        }
        public AdminMemberProvider(IUserRepository repository) : base()
        {
            _repository = repository ?? UserRepositoryFactory.GetRepository();
        }
        public User User
        {
            get;
            private set;
        }
        public UserAdmin.DataEntities.User CreateUser(string fullName,string passWord, string email)
        {
            return (null);
        }
        public override bool ValidateUser(string username, string password)
        {
            if(string.IsNullOrEmpty(password.Trim())) return false;
            string hash = EncryptPassword(password);
            User user = _repository.GetByUserName(username);
            if (user == null) return false;
            if (user.Password == hash)
            {
                User = user;
                return true;
            }
            return false;
        }
        /// <summary>
        /// Procuses an MD5 hash string of the password
        /// </summary>
        /// <param name="password">password to hash</param>
        /// <returns>MD5 Hash string</returns>
        protected string EncryptPassword(string password)
        {
            //we use codepage 1252 because that is what sql server uses
            byte[] pwdBytes = Encoding.GetEncoding(1252).GetBytes(password);
            byte[] hashBytes = System.Security.Cryptography.MD5.Create().ComputeHash(pwdBytes);
            return Encoding.GetEncoding(1252).GetString(hashBytes);
        }
    }
}

The key method implemented here is ValidateUser. This uses the Repository pattern to query the user by name and then compares the password in the repository with the password passed to the method. My default repository is backed by nHibernate, but it could be plain ADO or a fake repository for testing purposes. You will need to implement your own UserRepositoryFactory based on your data store. Note that I am encrypting the passed password before the comparison. This is because our passwords are hashed in the database.

Here is the Role Provider:


   public class AdminRoleProvider : RoleProvider
   {
       IUserRepository _repository;
       public AdminRoleProvider(): this(UserRepositoryFactory.GetRepository())
       {
           
       }
       public AdminRoleProvider(IUserRepository repository) : base()
        {
            _repository = repository ?? UserRepositoryFactory.GetRepository();
        }
       public override bool IsUserInRole(string username, string roleName)
       {
           User user = _repository.GetByUserName(username);
           if(user!=null)
                return user.IsInRole(roleName);
           else
               return false;
       }
       public override string ApplicationName
       {
           get
           {
               throw new NotImplementedException();
           }
           set
           {
               throw new NotImplementedException();
           }
       }
       public override void AddUsersToRoles(string[] usernames, string[] roleNames)
       {
            throw new NotImplementedException();
        }
       public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
       {
           throw new NotImplementedException();
       }
       public override void CreateRole(string roleName)
       {
           throw new NotImplementedException();
       }
       public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
       {
           throw new NotImplementedException();
       }
       public override bool RoleExists(string roleName)
       {
           throw new NotImplementedException();
       }
       public override string[] GetRolesForUser(string username)
       {
           User user = _repository.GetByUserName(username);
           string[] roles = new string[user.Role.Rights.Count + 1];
           roles[0] = user.Role.Description;
           int idx = 0;
           foreach (Right right in user.Role.Rights)
               roles[++idx] = right.Description;
           return roles;
       }
       public override string[] GetUsersInRole(string roleName)
       {
           throw new NotImplementedException();
       }
       public override string[] FindUsersInRole(string roleName, string usernameToMatch)
       {
           throw new NotImplementedException();
       }
       public override string[] GetAllRoles()
       {
           throw new NotImplementedException();
       }
   }
}

Again, many methods of the base class are unimplemented because I did not need the functionality.

Wiring the providers in web.config

This is all the code we need for the “model” level user authentication and role checking. Next we have to wire these classes up in our web.config so that the application knows to use them. The following should be inside of <system.web>:


<authentication mode="Forms" >
<forms loginUrl="~/LoginAccount/LogOn" path="/" />
</authentication>
<authorization>
<deny users="?"/>
</authorization>
<membership defaultProvider="AdminMemberProvider" userIsOnlineTimeWindow="15">
      <providers>
        <clear/>
        <add name="AdminMemberProvider" type="UserAdmin.DomainEntities.AdminMemberProvider, UserAdmin" />
      </providers>
</membership>
<roleManager defaultProvider="AdminRoleProvider" enabled="true" cacheRolesInCookie="true">
      <providers>
        <clear/>
        <add name="AdminRoleProvider" type="UserAdmin.DomainEntities.AdminRoleProvider, UserAdmin" />
      </providers>
</roleManager>

 

Brining in the Controller and View

The only thing left now is to code the controllers. First our LoginAccountController needs to be able to log users in and out of the application:


    public class LoginAccountController : Controller
    {
  UserMemberProvider provider = (UserMemberProvider) Membership.Provider;
        public LoginAccountController()
        {
        }
        public ActionResult LogOn()
        {
            return View();
        }
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult LogOn(string userName, string password, string returnUrl)
        {
            if (!ValidateLogOn(userName, password))
            {
                return View();
            }
  
            UserAdmin.DataEntities.User  user = provider.GetUser();
            FormsAuthentication.SetAuthCookie(user.UserName, false);
            if (!String.IsNullOrEmpty(returnUrl) && returnUrl != "/")
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return RedirectToAction("Index", "Home");
        }
        private bool ValidateLogOn(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName))
            {
                ModelState.AddModelError("username", "You must specify a username.");
            }
            if (String.IsNullOrEmpty(password))
            {
                ModelState.AddModelError("password", "You must specify a password.");
            }
            if (!provider.ValidateUser(userName, password))
            {
                ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
            }
            return ModelState.IsValid;
        }
 }

 

Here is our login form in the view:

<div id="errorpanel">
    <%= Html.ValidationSummary() %>
</div>
<div class="signinbox">
 
      <% using (Html.BeginForm()) { %>
    <table>
        <tr>
            <td>
                Email:
            </td>
            <td>
                <%= Html.TextBox("username", null, new { @class = "userbox"}) %>
            </td>
        </tr>
        <tr>
            <td>
                Password:
            </td>
            <td>
                <%= Html.Password("password", null, new { @class = "passwordbox"}) %>
            </td>
        </tr>
        <tr>
            <td>
                <input type="image" src="/Content/images/buttonLogin.gif" alt="Login" width="80" height="20"
                    border="0" id="Image1" name="Image1">
            </td>
            <td align="right" valign="bottom">
            </td>
        </tr>
    </table>
      <% } %>
 
</div>

 

That’s it. It’s really pretty simple.

Comments (35) -

Heavy Metal God
Heavy Metal God
10/16/2009 10:58:05 PM #

"I personally don’t feel comfortable having third party code create my user schema. Perhaps I’m being overly sensitive here."

Yes you are.  Smile

Stacey
Stacey
10/20/2009 4:23:33 PM #

Thank you so, so much for posting this. You have absolutely made my day in every possible way.

Kevin Jensen
Kevin Jensen
10/25/2009 7:21:39 PM #

nice article!  Can you display how you inject your custom principle/identity classes?

Atilla Malas
Atilla Malas
12/20/2010 2:36:54 PM #

Hi, is it possible that you also provide example or refer to a UserRepositoryFactory implementation. Thx.

cbmeeks
cbmeeks
1/7/2011 8:49:27 PM #

Thanks for this article.  I code in Rails at night but use Java/C# during the day.  In a recent project, I needed to add a custom membership provider to a ASP.NET/MVC app.  This helped a lot.

Thanks again!

Atilla Malas
Atilla Malas
1/11/2011 12:52:00 PM #

I started using this. In my case there is an issue, other than username,password there is also a third parameter identifying the repository to which this user belongs. Can you suggest how to add another parameter to this method. Thanks

mwrock
mwrock
1/12/2011 5:14:17 PM #

You could simply add a repository parameter to the controller method and provider validation.

Nirosh
Nirosh
8/18/2011 12:43:02 PM #

I understood your sample but still I am not very clear as to how I am going to use this code to limit user access to my controller actions..

Is there any attributes or something that I have to use..

Can you please extend your sample to explain that too??

Nirosh

vlada
vlada
8/30/2011 9:13:49 AM #

Hi,

this is really great article. I'am building site and I need good custom security and this lead me in the right direction. But I had some issues and questions:
1) When user is logged in is there any way to get UserID through whole application. For example in some controller I need UserID to get it's friends and type 'User.UserId'?
2) I see that this is older article and it works fine but did anything changed in meanwhile?

Thanks

Jon
Jon
9/21/2011 10:57:05 AM #

I don't think you need to call the base constructor on your constructor

iman
iman
10/13/2011 9:49:46 AM #

Hi, I', new in MVC and i want to use this to my project.

please tell me more about IUserRepository  and UserRepositoryFactory .


please show how implement these class . or post code of this class here so I can see,

tnx a lot.

Matt Wrock
Matt Wrock
11/13/2011 3:07:02 PM #

Jon: That is correct. If only I had been using Resharper in 2009!

Matt Wrock
Matt Wrock
11/13/2011 3:26:27 PM #

Hi Iman,

IUserRepository would be a interface defining CRUD (Create, Read, Update, Delete) methods against the repository (presumably a database) that stores the user info. This follows a typical Repository Pattern where by you encapsulate all persistence logic in a Repository class typically backed by an interface. The IUserRepositoryFactory is basically a "poorman's DI" (A method of dependency injection that does not use a full blown IOC container like StructureMap or Castle) solution to creating the concrete instance of IUserRepository.

See:
http://martinfowler.com/eaaCatalog/repository.html
www.codeproject.com/KB/aspnet/IOC_Works.aspx

This technique is fairly popular. I like it because it nicely isolates code that is specifically manipulating your data store.

The CodeProject Link above has a nice example of all of this but I am also considering updating this post with a more modernized sample.

Matt Wrock
Matt Wrock
11/13/2011 3:44:58 PM #

Hi vlada,

You can always access the user name in your app via HttpContext.Current.User.Identity.Name. If your ID is the same as the value in user name then this will work. Otherwise you would either want to store the ID in a cookie or do a lookup on your user repo querying on the name.

Regarding changes. This was written in 2009 against MVC v1. We are now on V3 and quickly approaching v4. To be honest, since I started working for Microsoft in the last couple years, I have been using federated authentication in windows live to manage site authentication and have not used Forms Auth since.

There is definitely some room for improvement here and I may post a more modern sample.

Matt Wrock
Matt Wrock
11/13/2011 3:54:58 PM #

Hi Nirosh,

No need for any attributes. Basically the snippet I have included from the web.config is what does the actual "wire up" of the login plumbing into your application.

chandra
chandra
1/6/2012 11:52:49 AM #

I am impressed by your work, but one question is coming to mind is that, suppose i have 6 controller function which have authorize attribute on it with roles. So if i use your method then for each authorization there will be hit on data base to check roles for that database. Is there any method where when user gets log in all the roles stored in Context.User and authorization attribute checks roles from Context.User not from GetRolesFromUser function, so that i can save round trip to sqlserver for checking roles

chandra
chandra
1/6/2012 12:06:17 PM #

See Joakim Method without using role provider,

stackoverflow.com/.../how-do-i-create-a-custom-membership-provider-for-asp-net-mvc-2
Is that also ok.

Do Authorize attribute read from cookie , if role provider is not implemented ?, because it is working.

Matt Wrock
Matt Wrock
1/6/2012 2:07:52 PM #

Hi Chandra,

Yes. The asp.net authorization logic does store the authorization ticket in a cookie. So if you do not use the role provider above, its just the initial login that requires a database lookup.

Rachid
Rachid
1/18/2012 4:58:54 PM #

Hey Matt

  What changes should i add if i have to use a custom user table in the database that have a diff structucture(extra information)

Matt Wrock
Matt Wrock
1/18/2012 5:10:15 PM #

Hi Rachid,

This pattern allows you to have any db schema that you want or to wire up the authentication to legacy schemas. In the sample I  provide in this post, the IUserRepository would handle all necessary logic to pull relevant user info and permissions from the correct tables and columns, whatever those might be in your app, and hydrate your User object with that data. Unfortunately I don't have a sample of what that might actually look like. It is rather straightforward but I think wouldbe helpful to provide. Been meaning to create a full fledged sample and put up on Github as a reference. Hopefully one day soon.

Rachid
Rachid
1/18/2012 5:19:13 PM #

Thanks Matt.

What i have is a table in my database calle UserAccount(UserI,Email,Password,FirstName,LastName)

i use Linq to SQL .Thus there is A UserAccount class generated from it.
How can use UserAccount?

Shoud i inherit it from MembershipUser or IPrincipal and override?

What properties to override?

Rachid
Rachid
1/18/2012 5:23:57 PM #

i have in AuthController:

public class AuthController : BaseController
    {
        //
        // GET: /User/

        FinkaynMembershipProvider provider = (FinkaynMembershipProvider)Membership.Provider;

        public ActionResult signin()
        {
            return View();
        }
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult signin(string email, string password)
        {
            if (!ValidateLogOn(email, password))
            {
                return View();
            }

            else
            {
                MembershipUser Myuser =(MembershipUser) provider.GetUser(email, password);
                
                return RedirectToAction("index", "Home");
            }
          
        }

        private bool ValidateLogOn(string email, string password)
        {
            if (String.IsNullOrEmpty(email))
            {
                ModelState.AddModelError("username", "Merci de  spécifier votre email.");
            }
            if (String.IsNullOrEmpty(password))
            {
                ModelState.AddModelError("password", "Merci de spécifier un pasword");
            }
            if (!provider.ValidateUser(email, password))
            {
                ModelState.AddModelError("_FORM", "Email ou password inconrect.");
            }
            return ModelState.IsValid;
        }


    }

and in the master page i have a link that changes depending on the user connection(if user connected show signout an if not connected show sign )

<a id="SignoutHref" runat="server">
              <%if(Page.User.Identity.IsAuthenticated){%>
                  <%=  Html.ActionLink(Page.User.Identity.Name, "Signout", "Home")%>
                <%} %>
               <%else{%>
            
                  <%=Html.ActionLink("Sign in", "signin", "Auth")%>
                <%}%>

              </a>

it show always Sign in.

Please help.

Matt Wrock
Matt Wrock
1/18/2012 5:38:40 PM #

Hi Rachid,

The key step you are missing is adding this to your else clase in signin(string email, string password):

FormsAuthentication.SetAuthCookie(MyUser.UserName, false);

This is the System.Web.Security call that actually creates an authentication ticket and cookies the user so that when they navigate from page to page, they stay logged in. In my sample I use the UserName property but you could use email or any unique identifier you have available.

Rachid
Rachid
1/18/2012 5:47:52 PM #

Thanks Matt.

How can i show the userName in the view?

how can i check in the view aspx code that a user is authenticated if UserAccount inherits from MembershipUser.

How can i store the userId in the cookie instead of username(my UserAccount Table contains UserName=FName+' '+LastName)

mwrock
mwrock
1/18/2012 7:14:26 PM #

Hi Rachid,

I'll admit it has been a long time since I have used this code and now (over 2 years later) I see errors in it that surfaced when I was copying it from my app and pasting it into this blog post.

Anyways, you will probably want to Create a UserPrincipal class that derrives from your own UserAccount class and implements IPrincipal.

In your view, your check on IsAuthenticated should succesfully return a boolean of whether or not the user is signed in. The FormaAuthentication infrastructure creates a FormsIdentity that has the Name you passed to the SetAuthCookie method as the value of its Name property.

You can use the provider's User property in your controller to get a reference to the UserPrincipal I just mentioned and then you have access to all of your user's info and can put what you need in the ViewData to be displayed.

As I look at my two year old code, I see things that I really don't like. Like the fact that you have to do a DB lookup on each request if you wanted to access the User property every time and also the fact that you cant access your own UserPrincipal's Identity from Page.User.Identity. I think that will just get you the FormsIdentity.

I'm gonna make a point of putting a new, updated post with a full F5 runnable code you can download from github. That should offer much more clarity.

Alex
Alex
3/17/2012 3:46:41 PM #

Thank you for your posting. Just let me go on and try to put it into practice and see. Because since two weeks I am tinking about customs membership, role and profile. But only confusion and nothing clear. I really like this and the way you explain. Is it also possible to use another base class for the User class? Taking from here how a custom prifile can be include?

Tim
Tim
5/2/2012 6:51:30 AM #

Hi Matt,
your post has helped me greatly understanding how to customize this process, however as you mention in your last comment recently that you would like to have your custom User available in the page's User property... How /where do i do this?

Cheers
Tim

mwrock
mwrock
5/2/2012 4:26:54 PM #

@Tim

To have a Page's User property reference your own custom User, you simply need to set the HttpContext's User property to your User. You also want to set Thread.CustomPrincipal to that user as well. In order for that to work right, your User must derrive from IPrincipal.

Matt

Tim
Tim
5/7/2012 11:51:01 AM #

Hi Matt,
      spent a while trying to consider the "best" way to architect this solution. I'm using entityframework backing onto a MSSQL database.
I've gone with storing a userid in session (as suggested www.bradygaster.com/...authentication-with-mvc-3.0) so that for all requests after initial authentication i can use a controllers which inherits from my basecontroller which overrides the OnAuthorization event and sets the user to my customPrincipaluser. Since my user table in the database has quite a few fields i created a view with only the most relevant fields, and a assocation in EF to get back to the main user object. So im not loading more data than needed by default.

Does this sound like the method you were considering? I've recently seen a a method using cookies (www.codethinked.com/setting-up-authentication-in-aspnet-mvc)

im wondering if its possible to combine both approaches to use cookies if possible falling back onto a session userid otherwise. Any thoughts

Cheers
Tim

fraude urssaf
fraude urssaf
5/8/2012 11:13:49 AM #

Nice

Prasanth
Prasanth
5/10/2012 1:05:14 PM #

Hi. great post and it was very useful . But it is hard to read code snippets that are not formatted. Might I suggest including gists of code in your posts ?

bogman
bogman
7/12/2012 4:25:36 PM #

Hi Matt,

Good blog. Would be v interested in seeing an updated example of the subject covered in this post with some f5 runnable code. Any update on this?

Been playing around with your example and would like to see an implementation of this showing how securing controllers to specific db roles without lookup each time could be best achieved..




Athil
Athil
9/7/2012 5:49:04 PM #

Very nice article. I’m just curious though; you only override ValidateUser(), so I can’t help wondering what you do for e.g. CreateUser() and ChangePassword(), the only other methods in the slightly aged MembershipProvider abstract that I am interested in. I have already implemented these in my MemberProvider class, which I access directly in my AccountController. Would you recommend I go the extra mile and override these in my CustomMembershipProvider as well

Athil
www.dailyfreecode.com/.../muhammed-athil-6458.aspx

Bala
Bala
10/25/2012 3:01:32 PM #

Hi,

I am implementing Repository Pattern in one of my project based on ASP.NET MVC4 and N-Tier Architecture. I am little confused as how to implement Custom Membership with Repository Pattern. Here are how I have implemented my classes so far.

Generic Repository

public interface IRepository<T> where T : class
{
void Add(T entity);
void Delete(T entity);
void Update(T entity);
IQueryable<T> GetAll();
T FindBy(Expression<Func<T, bool>> expression);
IQueryable<T> FilterBy(Expression<Func<T, bool>> expression);
}

Repository Base Class

public abstract class Repository<T> : IRepository<T> where T : class
{
private STNDataContext _stnDataContext;
private readonly IDbSet<T> _dbSet;

protected Repository(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
_dbSet = StnDataContext.Set<T>();
}

protected IDatabaseFactory DatabaseFactory { get; private set; }
public STNDataContext StnDataContext
{
get { return _stnDataContext ?? (_stnDataContext = new STNDataContext()); }
}
public void Add(T entity)
{
_dbSet.Add(entity);
_stnDataContext.Commit();
}

public void Delete(T entity)
{
_dbSet.Remove(entity);
}

public void Update(T entity)
{
_dbSet.Attach(entity);
_stnDataContext.Entry(entity).State = EntityState.Modified;
_stnDataContext.Commit();
}

public IQueryable<T> GetAll()
{
return _dbSet.ToList().AsQueryable();
}

public T FindBy(Expression<Func<T, bool>> expression)
{
return FilterBy(expression).SingleOrDefault();
}

public IQueryable<T> FilterBy(Expression<Func<T, bool>> expression)
{
return GetAll().Where(expression).AsQueryable();
}

User Repository Interface

public interface IUserRepository : IRepository<User>
{
}

User Repository

public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
}
}

Heres my Business Logic Layer

Generic Service Interface

public interface IService<T>
{
void Add(T entity);
void Delete(T entity);
void Update(T entity);
IEnumerable<T> GetAll();
T FindBy(Expression<Func<T, bool>> expression);
IEnumerable<T> FilterBy(Expression<Func<T, bool>> expression);
}



Service Base

public class Service<T> : IService<T> where T : class
{
public void Add(T entity)
{
throw new NotImplementedException();
}

public void Delete(T entity)
{
throw new NotImplementedException();
}

public void Update(T entity)
{
throw new NotImplementedException();
}

public IEnumerable<T> GetAll()
{
throw new NotImplementedException();
}

public T FindBy(Expression<Func<T, bool>> expression)
{
throw new NotImplementedException();
}

public IEnumerable<T> FilterBy(Expression<Func<T, bool>> expression)
{
throw new NotImplementedException();
}
}



User Service Interface

public interface IUserService : IService<User>
{
}

User Service Implementation

public class UserService : Service<User>, IUserService
{
private readonly IUserRepository _userRepository;
private readonly IRoleRepository _roleRepository;

public UserService(IUserRepository userRepository, IRoleRepository roleRepository)
{
_userRepository = userRepository;
_roleRepository = roleRepository;
}

public IList<User> GetAllUsers()
{
return _userRepository.GetAll().ToList();
}

public User GetUser(int id)
{
return _userRepository.FindBy(x => x.UserId == id);
}

public User GetUser(string userName)
{
return _userRepository.FindBy(x => x.Username.Equals(userName));
}

public void CreatUser(User user)
{
_userRepository.Add(user);
}

public IList<User> GetUsersForRole(string roleName)
{
return _userRepository.FilterBy(x => x.Role.RoleName.Equals(roleName)).ToList<User>();
}

public IList<User> GetUsersForRole(int roleId)
{
return _userRepository.FilterBy(x => x.Role.RoleId == roleId).ToList<User>();
}

public IList<User> GetUsersForRole(Role role)
{
return GetUsersForRole(role.RoleId);
}

}
Hi Bob,
I am developing one application using ASP.NET MVC4, N-Tier Architecture and Repository Pattern. I want to implement Custom Membership functionality and refering your gpsnerd application as reference. I am little confused implementing the same. Can you help?

I am not very sure on Am I going the right way? If yes how do i implement my UserService class. 

If no, what changes do i need to implement.

Any help on this is highly appreciable. Thanks in advance

Romias
Romias
10/31/2012 4:05:12 PM #

Hi Matt, any way to pass more parameters to a RoleProvider?
The RoleProvider get the user roles just using the username, and I need to use the username and TenantId to verify roles.

Any idea on how to accomplish this?
I could search the database myself, but MVC will try to use the RoleProvider.

Pingbacks and trackbacks (1)+

Add comment

biuquote
  • Comment
  • Preview
Loading

About Me

Hey thats me!

I'm Matt Wrock with over thirteen years of experience architecting scalable, distributed, high traffic web applications. I currently live in Woodinville, WA with my wife, two daughters, three dogs and cat. I work for Microsoft as a Sr. Software Engineer working in Cloud Developer Services. I'm also project founder and owner of http://www.requestreduce.org and a committer to http://chocolatey.org.

Month List