Skip to content

Announcing MembershipReboot

July 29, 2013

It’s sort of silly that I’m doing an announcement now since MembershipReboot is at version 2.1 (my first release was in January, 2013), but since I never made a formal post on it, this will have to suffice.

It’s no secret I’ve been a harsh critic of the ASP.NET membership system (since ~2005 when it was first released) and have never been able to use membership on any real projects (either due to requirements of the project where membership didn’t match or the design of membership being was too leaky of an abstraction). Since I’ve been building these sorts of libraries over and over for many years now, I finally decided to write a more formal library and open source it for others to use (and contribute to). From the readme:

MembershipReboot is a user identity management and authentication library. It has nothing to do with the ASP.NET Membership Provider, but was inspired by it due to frustrations with the built-in ASP.NET Membership system. The goals are to improve upon and provide missing features from ASP.NET Membership. It is designed to encapsulate the important security logic while leaving most of the other aspects of account management either configurable or extensible for application developers to customize as needed.

Some of the features of MembershipReboot are:

  • single- or multi-tenant account management
  • flexible account storage design (relational/SQL or object/NoSql)
  • claims-aware user identities
  • support for account registration, email verification, password reset, etc.
  • account lockout for multiple failed login attempts (password guessing)
  • extensible templating for email notifications
  • customizable username, password and email validation
  • notification system for account activity and updates (e.g. for auditing)
  • account linking with external identity providers (enterprise or social)
  • proper password storage (via PBKDF2)
    • configurable iterations
    • defaults to OWASP recommendations for iterations (e.g. 64K in year 2012)
  • Two factor authentication support via mobile phone SMS messages

The most common use case will be to integrate this into an ASP.NET or ASP.NET MVC application, though the library can also be used over a network as a service.

This most recent release (v2.1) had a bit of internal refactoring and now I’m quite happy with the architecture. It’s quite flexible and can accommodate most (if not all) enterprise-level requirements for user account management. I think one of the important features of MembershipReboot (as stated above) is that it was designed to encapsulate the important security logic, while leaving most of the other aspects of account management extensible for application developers to customize as needed. In other words, MembershipReboot does all the proper password hashing, authentication logic and other important security stuff, but if you want to change how the data is stored or how emails look when they are sent, that’s all open to customization. This lack of this separation was one of my biggest complaints about the ASP.NET membership system — it didn’t do the hard parts and allow you to extend the simple parts. MembershipReboot does.

The code is available on github (as well as a few samples to you can see how to use and extend the API). Feel free to provide feedback, questions, and enhancement requests on the issue tracker. It’s also available on NuGet.

Enjoy.

37 Comments leave one →
  1. RonyK permalink
    July 30, 2013 1:49 am

    Thanks! Looks interesting and very useful. Are there any thoughts about basing ThinkTechture IdentityServer on this framework instead of Membership?

    • July 30, 2013 8:57 am

      They do different things, so you’d pick one or the other depending upon your needs. Our plan is to eventually use MR in IdentityServer (by default) for the user account storage.

  2. July 30, 2013 1:58 am

    Reblogged this on http://www.leastprivilege.com.

  3. panesofglass permalink
    August 6, 2013 9:37 am

    This is really great stuff. Quick question: for external identities, can you use a federated identity? Does it matter? Your example only shows OAuth, thus the question. Am I getting things mixed up again?

    • August 6, 2013 10:08 am

      Yep, there are APIs to “login with a linked account” and these are meant to be general so that it works with social or ws-fed style (which is what you’re asking). You need to have an identifier for the “external provider” and a identifier for the user that’s unique to your provider (so in WS-Fed a NameID claim would be ideal).

  4. panesofglass permalink
    August 6, 2013 9:46 am

    Looking at your notes above, I think the “enterprise” identities relate to federated identities. Do you have a sample for making this connection?

    • August 6, 2013 10:14 am

      No sample, no. WIF (FAM and SAM) normally take care of all of the “logging in stuff”, so if you then wanted to use MembershipReboot to create a local account for the federated identity then you’d prolly want to hook some of the FAM events and in there do some logic to create the local account. I guess the hard thing sort out is your app’s logic for mapping the federated identity to a local account in MembershipReboot. The logic in the AuthenticationService.SignInWithLinkedAccount is trying to do that in a reasonable way, but it’s also trying to issue the SAM cookie. In your case you don’t need it to do the SAM cookie, so you might just need to reproduce the account creation logic (or customize it as needed) in your FAM event handlers.

      I’ll add this as an issue on github since it’s an interesting use case and I’ve not thought thru all of possibilities. Perhaps I’ll produce either a sample or a helper API to satisfy this scenario.

      • panesofglass permalink
        August 6, 2013 10:19 am

        Great! Last question: this is all cookie based? What if I then wanted to use tokens after authenticating?

        • August 6, 2013 10:25 am

          Depends what you’re doing. Again, the main point of MR is to manage identity (as in a database) and authenticate those identities. There are then helper APIs for establishing a login session and this is implemented in terms of the SAM (so yes cookies are used). But if you’re not doing a browser based app, and building a WebAPI (for example) then you could use MR for the identity storage and use basic Auth for the authentication (if that makes sense). You might do this if you were using IdentityModel’s session token feature — the initial authentication uses basic auth and you need a database to check credentials — MR would be that database, but then yes, the rest of the access to your app would use a token. (I hope all of that makes sense)

          • panesofglass permalink
            August 6, 2013 12:01 pm

            That all makes sense. What about generating the token? Can I get that from the SAM cookie? Would I want to implement my own AuthenticationService to return the token rather than storing that in a cookie?

          • August 6, 2013 1:37 pm

            Token is up to you. Sounds like you want to use Identitymodel’s session token stuff for that. You’d just use MR to check credentials and load claims.

  5. panesofglass permalink
    August 6, 2013 12:41 pm

    I’m a liar. I have more questions. :) Does this build on the structure of the Membership database tables, or does it use an entirely different set of tables?

    • August 6, 2013 1:36 pm

      All new tables. You can store them in EF or in any other DB you want (NoSql, for example).

  6. August 6, 2013 9:06 pm

    Hi Brock,
    Firstly – thanks so much for creating this library – I think it looks fantastic! I’m currently trying to use it in conjunction with Thinktecture.IdentityModel in a VS 2013 MVC + Web Api app,which uses basic auth for the web api (since I can’t quite get token based auth working due to uncertainty on how to issue one) and forms auth for the MVC parts. I have a couple of questions:

    – Is is possible to add additional data to a user profile ? e.g. if I wanted to store the user’s address alongside the rest of their profile details, how would I go about it?

    – Where do I configure the options for account verification etc. I looked in the samples but couldn’t find where this was happening

    – Is it possible to completely replace the email bodies with my own implementation? E.g. I currently use MvcMailer to send Razor-templated emails. I checked your customization sample, and gather I can use a CustomEmailMessageFormatter for this, but I just wanted to confirm that it would be the right place to do this.

    Thanks heaps and great work,
    Phil

    • August 6, 2013 9:10 pm

      Apologies for my second question, I’ve figured that out now – the MembershipRebootConfiguration class. Just the other two to worry about :-)

    • August 6, 2013 9:24 pm

      I’d suggest going thru the “Single Tenant” sample — it shows all the various operations (login, logout, change email, etc.), as well as adding claims for a user. Claims is where you’d store the extra identity stuff. I guess some blog posts would be good to document some of the features in detail :)

      Also, check out the AuthenticationService — this replaces forms auth in favor of WIF’s SAM. See this for what it’s doing:

      https://brockallen.com/2013/01/26/replacing-forms-authentication-with-wifs-session-authentication-module-sam-to-enable-claims-aware-identity/

      Account verification can be enabled/disabled via the MembershipRebootConfiguration.

      Yes, you can replace the email bodies, but it requires either: 1) changing the code in MR itself (check in the Notification folder), or 2) registering your own event handlers for the various activity on the user account — IOW, look at the EmailNotificationEventHandlers.cs file and use your own.

      If your email templating engine is good, then I’d be happy to have you contribute or provide an extension :)

      • August 6, 2013 9:49 pm

        Wow thanks for such a prompt and detailed reply! Ah I see the claims part now – so if I understand correctly, each additional claim ends up being stored as a row in the UserClaims table. E.g. If I store a user’s postal code, it would get a row with type=PostalCode (or the equivalent representation), and value=123456 or something like that. Would this be the best way to do it, or should I create a separate table and object model to handle additional user data? I note that the system.security.claims.claimtypes class has values for postal code, but I can imagine situations where the data I’m storing might not map onto one of the standard claim types.

        I take it all the claims are retrieved in a single query (so no performance penalty for iterating through them etc)?

        For anyone else that’s reading – w.r.t. the setting of the claims – check out Areas > UserAccount > Controllers > HomeController .cs in the single tenant sample. e.g.
        var account = userAccountService.GetByUsername(User.Identity.Name);
        account.AddClaim(ClaimTypes.Gender, gender);

        The authentication service looks great – I figured that you were pretty much doing everything you said in that blog post within the auth service, but it helped clear things up once I saw it all in action.

        Re the email part – I’ll see if I can get MvcMailer working with it, and if all goes well I’ll try and submit something. I’ve found MvcMailer perfect for this in the past – Scott has a good post on it at http://www.hanselman.com/blog/NuGetPackageOfTheWeek2MvcMailerSendsMailsWithASPNETMVCRazorViewsAndScaffolding.aspx if you’re interested.

        • August 6, 2013 10:00 pm

          How to use claims is up to you. MR is meant to be general purpose, so it allows arbitrary claims. If you want a dedicated column in your object model for something then you can always derive from UserAccount, add the extra properties/columns and implement your own repository.

  7. August 8, 2013 9:48 pm

    Hi Brock,
    Sorry for bugging you again, but have you used MembershipReboot in conjunction with Thinktecture.IdentityModel before? I’m trying to do just that, and am running into issues where the claims that are loaded using forms auth for my mvc site (e.g email) are missing when I authenticate with basic auth (using Thinktexture.IdentityModel’s basic auth functionality) in the web api.

    I see that in the MembershipReboot single tenant example, when a user is logging in, the controller calls the AuthenticationService SignIn method, which adds all of the additional claims and then uses the SessionAuthenticationModule to write a cookie containing those claims. I was hoping that on subsequent requests the additional claims would have been loaded automatically regardless of whether I used basic auth via the api, or cookie based auth for the mvc part of the site, but it appears not. Is this somewhere I could plugin to the basic auth pipeline (or session token auth) for web api where I could load in the additional claims from MembershipReboot?

    I tried adding a call to AuthenticationService.SignIn() in my credential validation method for Thinktecture.IdentityModel’s basic auth as follows, but it didn’t seem to do the trick….

    var authConfig = new AuthenticationConfiguration
    {
    RequireSsl = false,
    EnableSessionToken = true
    };

    // setup authentication against membership
    authConfig.AddBasicAuthentication(Verify);
    ……..

    public static bool Verify(string user, string pass)
    {
    var accountService = ServiceLocator.Current
    .GetInstance();

    var authSvc = ServiceLocator.Current
    .GetInstance();

    BrockAllen.MembershipReboot.UserAccount account;
    var isAuthenticated = accountService.AuthenticateWithUsernameOrEmail(user, pass, out account);
    if(isAuthenticated){
    authSvc.SignIn(account);
    }
    return isAuthenticated;
    }

    Any ideas?

      • August 9, 2013 5:39 pm

        Thanks Brock. I’ve read through as many blog posts and wiki entries as I could find on basic auth, but am still a bit stuck. Would I be right in thinking I need a custom ClaimsAuthenticationManager which I use to call the MembershipReboot AccountService to gather the additional user claims and load them into the current principal? The examples I’ve found in the samples folder for Thinktecture.IdentityModel don’t cover this specific scenario, although the FormsAndBasicAuth sample does contain a custom ClaimsTransformer which looks like it might be doing something similar (https://github.com/thinktecture/Thinktecture.IdentityModel.45/blob/master/Samples/FormsAndBasicAuth/FormsAndBasicAuth/ClaimsTransformer.cs)

        Currently, basic auth and session auth are working with Thinktecture.IdentityModel with the configuration as at https://gist.github.com/phillee007/6197398 (Note I’ve tried using the FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager to see if it would load additional claims from the cookie when using a web client, so this may be incorrect).

        • August 9, 2013 6:13 pm

          Yep — the session endpoint in IdentityModel just does uid/pwd. You’d then need a ClaimsAuthMgr to do the claims transformation and you’d fetch those from MR.

          • August 10, 2013 11:44 pm

            Magic – thanks Brock! Whilst I initially got that working on postauthenticaterequest by copying some of the code from the AuthenticationManager to load claims, I ended up removing it again once I started caching session tokens using the samples provided with Thinktecture,IdentityModel. Given that I was loading the additional claims when the user signed in (as shown in the Verify method in my earlier comment), from then on the claims were all stored/loaded automatically on each request, so I didn’t have to manually load them after all. I’m glad that both IdentityModel and MembershipReboot work well together, and have so much flexibility built in :-)

  8. August 21, 2013 8:26 am

    Hi Brock,

    In our case my MVC app connects to WCF service residing in the same machine but different http address. How can i use pass-through authentication.

    MVC connects to WCF connects to DB

    Mobile Apps connect to WCF directly

    Mobile Apps will need to authenticate with WCF service but browser user has already authenticated with MVC hence there needs to be a pass through authentication between MVC and WCF.

    Any ideas..?

    Thanks

    • August 21, 2013 9:35 am

      WCF can accept something called a delegation token. But this requires the service to use the ws2007FederationHttpBinding and requires the use of a STS. This also would require your MVC app to federate with the STS. In short, since you’re talking about different apps (the MVC app and the WCF service) needing to authenticate the same users against the same identity store, this is why you introduce federation.

      The other approach is to make the MVC app collect the credentials and then replay them to the WCF service.

  9. September 12, 2013 12:15 am

    I just noticed there is a new membership system called ASP.NET Identity. Do you know if that is built on top of the existing leaky Membership abstraction? Or is it something new?

    • September 12, 2013 12:34 am

      It’s new and coincidentally looks a lot like MembershipReboot. The first version will be very spare, though more features will come eventually.

  10. Zvjezdan permalink
    October 3, 2013 3:47 am

    Great work. I’ll try to do NHibernate support.

  11. December 5, 2013 2:15 pm

    Hello, First of all, thank you for thinking through so many scenarios. The multi-tenant scenarios are so common now and I am surprised to find Microsoft hasn’t implemented that in the ASP.NET Identity. I wanted to see if you had any samples of integrating this with the WebAPI 2.0? I am primarily interested in generating Bearer Tokens that users can pass to authorize themselves. Are there any specific configuration items that need to be addressed? Any help from your side would be very highly appreciated.

    • December 7, 2013 3:25 pm

      Ok, I’ll try to find some time for a sample. But to be honest, the hard part is understanding the right OAuth2 flow and then how the middleware implements it.

  12. yonghan79 permalink
    January 11, 2014 1:13 am

    Hi,thanks for the membership,is it possible to use it in pure asp.net web api application and having relationship with other table?Thanks

  13. Alexey Auslender permalink
    March 23, 2015 12:50 pm

    Hello Brock,we are currently migrating our membership provider db to new authentication provider .We want to take advantage on memebershipreboot and I am trying to find some sample how to migrate data.The only one I have found is http://www.asp.net/identity/overview/migrations/migrating-an-existing-website-from-sql-membership-to-aspnet-identity.So may be in our case we should migrate to Asp.Net Identity and then use your Identityreboot or is it possible to migrate to memebershipreboot?

    • March 23, 2015 1:04 pm

      Well, migration is possible to any system. The hard part is passwords — if they’re hashed in the old system then you need to figure out how you’ll handle them. One approach is to keep the old password format and then re-hash as users login (where you have the plain text passwords). But yea, it’s all possible — you just need to write some code :)

  14. stefanveliki permalink
    April 9, 2015 6:00 pm

    Hello Brock,
    Was there a reason to not have a “RemoveEventHandler”, as opposite to “AddEventHandler”?

    Also, what are your thoughts execution order of event handlers? I am attempting to force the execution of a handler defined in a derived project ahead of the handler which is defined in the base project.

    Thank you

    • April 11, 2015 9:48 am

      Please ask issues related to the code on the github issue tracker. Thanks

Trackbacks

  1. An Interesting List of Development Stuff (February 2014) | rionscode
  2. An Interesting List of Development Stuff (February 2014) - Rion.IO

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: