Friday, November 26, 2004

In my previous Microsoft.ScalableHosting post I described with some excitement the .NET 1.x Membership API found hidden inside of DotNetNuke 3.0.  My excitement is mostly about using the API outside of DNN.  There is also excitement about the possibility of DNN supporting alternate authentication stores through the provider model and the improved API.  Windows authentication is something missing from previous DNN that may work well now.  I have not yet tried it.

 

2 days ago a document describing Membership usage inside of DNN was posted.  The reading of that document prompted several thoughts which I will share here.

 

Why use Microsoft’s Membership API? 

One of the important things the API does for us is standardize how authentication is handled in ASP.NET.  With everyone cooking up their own solutions we constantly are learning a different authentication methodology and system with every application.  With standardization of the API we can now use front end tools like the Login controls in ASP.NET 2.0.  As long as developers stick with the base API’s, controls can be written against them and have high levels of assurance that they will work consistently.

 

Second, an equally important feature of Membership is the underlying provider model for implementation.  With a provider back-end developers are able to completely customize their data storage and data access as it related to Membership.  This design pattern also allows every application that uses the Membership API to share user accounts and roles when so desired.  How many times have we heard the complaints that ASP.NET Forums doesn’t use the same authentication system as DNN, Rainbow and IBS.  As these systems are updated to support the Membership API the only thing required to share authentication will be a configuration line in the web.config to point them all at the same provider.

 

DNN 3.0 Membership description falls short!

The reason for this whole post is to share my belief that DNN 3.0 falls short in its implementation of Membership or at least the description of such.  Throughout the DotNetNuke Membership document I was disturbed by the logic I discovered.  In the section DotNetNuke & Whidbey (Page 1) Shaun Walker says that DNN must avoid creating its own provider to simplify future migration.  This is absolutely backward and representative of other architecture decisions that continue to pain DNN.  DNN already has a robust solution for membership data.  Apparently there are business rule dependencies at the data level in some places (Page 7, UserRoles).  DNN is classic textbook example of an application that should create its own membership providers for use with the API.  By doing so other applications could share DNN’s user base easily and DNN would not be forced to make as many core changes.

 

On one hand I am glad to see that Satellite tables have been created in DNN to separate Membership data for DNN specific user/roles data.  On the other hand, the integration issues should have nothing to do with the underlying data storage and instead be addressed in the DNN API’s.  With a custom DNN Membership system both the Microsoft Membership API and the DNN specifics could easily be combined.  This would allow DNN to use any Membership provider while still supporting all of the DNN specifics and managing synchronization issues.  Page 9 CBO (Custom Business Objects) is exactly the right approach.  I believe the issues are being addressed, but the failure to see things from a Domain (objects, services) perspective really bothers me.  DNN thinking still tightly couples Data structures and the API’s and the 2 should be considered separately. 

 

Sharing users between portals is now a simple matter of sharing the ApplicationName that the Membership provider uses.  To make users unique all that is needed is to use unique ApplicationName’s.  The scenarios presented in Deployment Scenarios on Pages 1-2, Data on Page 3 and Multiple Portals on Page 10 do not make much sense.  By standardizing on the API, any deployment should be supported as long as a provider is used that supports the desired scenario.  The provider is the implementation and it is the key to supporting different scenarios.  Thankfully MembershipProvider.ApplicationName can be changed on each request enabling support for multiple portals with or without shared authentication systems.  By using multiple providers it is even possible to use different authentication stores for each portal in a DNN multiple portal scenario.

 

In Development Tasks (Page 2) it is apparent to me that there is a significant misunderstanding of Membership that must be clarified.  It is true that the UI controls built on top of Membership have not been back ported.  But there is a clear separation of the API and the SQL DB & Provider.  The API and the Provider should not be confused.  They must be considered as independent components.  Sure they work together, but the API does not in any way dictate underlying provider or storage that is used.  See the sidebar on the Membership API architecture at the end of this post.

 

When I hit the Data section at the end of page 2 and beginning of page 3 hope was returned to me.  Separation of responsibility is acknowledged between DNN data and the Membership data.  This is absolutely critical in order to use providers that only give DNN access to the data through the API and not in the database.  This separation creates a new paradigm for some developers.  It requires business rules to be placed in code and not in stored procedures.  The code has full access to the data through the API, but the database has no access to it or knowledge of it unless developers violate the intended usage.  If you are used to putting rules in your database it’s time to quit it!  You are asking for trouble with Membership data if you do this.  Thankfully DNN has fully acknowledged this in the Membership area.  Now we all need to be building software with the principles of separation of responsibility from the foundation.

 

Custom Attributes on Page 4 shows more confusion about the API and Provider.  It leads me again to believe futher that a DNN provider and custom DNN system would be best.  The custom attributes of course would still have to be treated as DNN specific data, but that is acceptable and exists no matter how Membership is implemented.  (DNN developers, I am not suggesting you replace Micrsoft's Membership, merely that you wrap it to support your additonal features.)

 

As a final note I want to publicly state that even though I have been critical of DNN here I am mostly seeing a reflection of my own failures in software development.  Now I desire to improve my own practices and help others see the benefits of good architecture and patterns as well.  With DNN’s prolific exposure it deserves a careful critique so that its best practices are adopted and its worst avoided.  This critique in no way reflects my opinions of Shaun Walker or any DNN developers.  Shaun and I have met personally and I enjoyed my meeting with him.  I also have great respect for him and his work in the ASP.NET community through the DNN portal.  Some readers may also know that I work with Rainbow Portal.  I choose not to be a fanatic about software and this post is neither for nor against DNN.  Each person must choose the solution that is best for them.

 

Sidebar: UserName and RoleName are not updatable through the Membership API as indicated in the DNN document.  The SQL database that goes with the default SQLMembershipProvider does not prevent you from updating UserName or RoleName through your own tools.  All relationships within the database are done by unique id’s so changing names will not break anything.  If you desire to change these names you must simply take care to update your own systems that may reference them and also to ensure that you keep them unique per application.  (Application referring to the definition of application inside of the SQL Membership database.)  Perhaps Microsoft should expose UserId and RoleId through the API so that we can store those in our applications instead of storing username and rolename as the unique ID’s provided through the API currently. 

 

Let me suggest a Membership best practice here.  Windows authentication uniquely identifies users as DOMAIN\USERNAME.  When using the Membership API with forms authentication I suggest always using APPLICATIONNAME\USERNAME.  By doing that you will be able to uniquely reference Membership users throughout your systems and not have to worry as much about username conflicts that could occur otherwise.

 

Sidebar: A look at the Membership API architecture reveals a nice model that may fit many other areas well.  MembershipUser is a simple entity to work with.  Its functionality is limited a single are of responsibility.  It knows nothing about the data store it is persisted in or even if it is persisted.  It also knows nothing about the collections it may be contained in (Roles).  To interact with an underlying storage system the Membership API acts as a service layer between the MembershipUser entity and a persistence Provider.  The Provider layer allows for complete control of the Membership implementation as it interacts with a persistence store.  The store may be a database, flat files, memory stream, or anything a developer chooses to be the implementation of the fields and methods required by the Membership system.  Developers are given a standard API to program against without concern for the underlying database.  The database designers do not have to be concerned with the API or the Domain entities that may represent the data.