Tuesday, May 30, 2006

Membership Forms Auth Integration with Community Server

I recently set out to solve the following problem:

I have a community site running CommunityServer as one application running under a sub-domain url and acting as part of the site (in a different app) running on the base domain (and www sub-domain). The community site is running Community Server 2.0 and is setup to use forms authentication for Membership, along with Roles, and Profiles. CS uses the new provider model for asp.net 2.0, utilizing custom CS providers. The primary site is also asp.net 2.0 and is using the built-in .net framework sql providers. I want to be able to login or create a user in either site, and have that login and profile carry over to the other site.

The basic information you need to make this happen in .net forms authentication is straightforward, and is covered here. Make those changes first, then there are just a few adjustments to make to the base site and two tweaks to Community Server to complete the fix.

First, Community Server. The CS providers are configured in the web.config of the CS site. In order to have CS draw from the same Membership data as your base site they must share the application name attribute. You can change this in the web.config section for each provider. However, this isn't likely to change anything in CS. You also need to go into the CS database and change the ApplicationName column value in the cs_SiteSettings table. I couldn't find this as a configurable setting anywhere else in CS. The only other change to make to CS is to set the cookie domain to the same domain as the other site. Obviously this only works if our problem space is dealing in different sub-domains. To do this in the CS control panel go to AdministrationMembershipCookie and Anonymous Settings area and set the Cookie Domain value.

Once you have made these changes to CS, you'll need to restart the application pool.

At this point the two sites should allow login's from one site to carry over to the other (if you have cookies from earlier attempts, you'll need to delete them). The only problem is that if you allow new logins to be created at both sites, you'll need to align new member creation on the base site with member creation on the CS site. One way to deal with this issue is to direct member creation over to your CS site. I wanted my primary site to be self-contained so I decided to create CS-capable logins in the primary site.

First, the CS profile configuration has numerous attributes defined. You should add these to your profile configuration section of your main site's web.config. Second, CS has its own user and profile tables in the database that store additional community data for the standard Membership user created by the provider. The "right" way to do this may well be to create a custom provider. Like any good developer I have a lazy streak that set me looking for a quicker way to accomplish this. I did try to just use the CS providers in my main site. Unfortunately, these providers depend on way too many configuration settings and file locations based on the CS site for this to be worthwhile. What I settled on for now is to add the additional rows to the database in a handler for the CreatedUser event of my CreateUserWizard control. This does require a hack that is probably better dealt with by creating a custom provider, but I'll do that when I have more time.

For now there are two CS tables: cs_Users and cs_UserProfile which need new rows based on the created Membership user. Most of the columns have default values, but you'll need to supply the Membership based ID to both tables, and the new CS based UserId to the UserProfile table (FK), along with the proper SettingsID. Along with this, you need to add this new user to the proper roles associated with CS. By default new users are in the 'Everyone' and 'Registered Users' roles. A simple call to Roles.AddUserToRoles will take care of that.

The hack centers around what to do if the CS database updates fail. The problem is that the Membership operation already completed succesfully (which it must for you to have the FK data for cs_Users) and you are in an event that can no longer cancel the operation. Thus, you need to deal with rolling back the new user yourself and editing the CreateUserWizard messages and diplay format which the website user sees. In theory, a custom membership provider would have access to the create operations in time to deal with providing an error to the control. In this case, I just run the updates in a transaction and delete the new membership user if there is a failure during the cs table inserts. Finally, you have to set custom error text, and eliminate the success message from the control. Good enough to get going. I'll write more when I create the custom provider.

PS: A good article on how the dynamic profile properties are provided to intellisense is here.
Submit this story to DotNetKicks

No comments: