Integrate DNN with ADFS without writing a new Authentication Provider

Posted by Unknown on Tuesday, January 11, 2011

The partner organization already had ADFS setup. The goal was to have the partner organization authenticate the user and then have DNN automatically recognize the user, not having to enter any more credentials. From there we wanted to manage the groups and permissions through the regular DNN portal.
I was initially under the misconception that I could not use the current DNNMembershipProvider and I would have to create a whole new Authentication Provider and collect the credentials and pass them to my partner organization.

What I have figured out is that you CAN use the existing DNNMembership provider, but you have to create the user before the request gets to the DNNMembership provider using the information provided by ADFS.

Note: This method is a straight pass-through from ADFS to DNN. No measures are taken to authenticate the user or to check any of the inputs from ADFS.

As an aside, this method works not only with ADFS but also any other external 3rd-party Authentication method that sends user information in headers or cookies. Change the context_AuthenticateRequest method to suit your requirements.

Here are the pre-conditions to this article. I expect:

* Your partner organization has ADFS up and running
* You have your ADFS proxy up and running and it talks to the partner organization.
* ADFS Web Agent is installed on the server you are running.

Let’s get started.

What you have to do is create an HTTPModule that will intercept the AuthorizeRequest events before DNN does. An over-simplified definition of an HTTPModule is, it is a piece of code that runs before any page gets hit. You can listen for a lot of different events. For more information, click here.

To create the HTTPModule you will need to:

* Open Visual Studio and create a new class library.
* Create a new class and copy the code attached to this blog.
Add References to the following DLLs
o DotNetNuke
o System.Web
o System.Web.Security.SingleSignOn
o System.Web.Security.SingleSignOn.ClaimsTransform
o System.Web.Security.SingleSignOn.Permissions
* Compile the solution
* The DLL that is created will need to be placed in the bin directory of your website.
* Make changes to your web.config (explained in a later section).

I will walk through a bit of the code. Here is the first snippet of code. We start listening for the AuthenticateRequest event. All other events pass through untouched.
view source
1 public void Init(HttpApplication context)
2 {
3 //Start listening for each authentication request
4 context.AuthenticateRequest += new EventHandler(context_AuthenticateRequest);
5 }

Now, what do we do when this event is fired off? To shorten the namespace I added the statement
view source
1 using SSO = System.Web.Security.SingleSignOn;

First off, we need to get the information that ADFS has sent us by casting the User.Identity into the ADFS object SingleSignOnIdentity.
view source
1 public void context_AuthenticateRequest(object sender, EventArgs e)
2 {
3 HttpApplication app = (HttpApplication)sender;
4 //By the time the request gets to here, it should have been authenticated
5 //against ADFS.
6 SSO.SingleSignOnIdentity id = (SSO.SingleSignOnIdentity)app.User.Identity;

At this point you will have access to the user’s Identity and any claims coming from the ADFS server. You can access them through id.SecurityPropertyCollection. You can use them to populate the new user account. You can iterate through the claims with the following code
view source
1 foreach(SecurityProperty sp in id.SecurityPropertyCollection)
2 {
3 Console.WriteLine(sp.Name);
4 Console.WriteLine(sp.Value);
5 }

Next, we check to see if the use already exists in the database by using the DNN API function GetUserByName. If it doesn’t, then the user is created by the standard DNN API function CreateUser and logged in. If the user does exist already then we log them in automatically. The user will automatically be added to the Registered Users and Subscribers security groups.
view source
01 //'See if user exists in DNN Portal user DB
02 UserInfo objUserInfo = UserController.GetUserByName(currentPortal.PortalId, id.Name);
03 //' user does exist - try to create on the fly
04 if (objUserInfo == null)
05 {
06 objUserInfo = new UserInfo();
07 objUserInfo.DisplayName = id.Name;
08 objUserInfo.FirstName = id.Name;
09 objUserInfo.LastName = id.Name;
10 objUserInfo.Username = id.Name;
11 objUserInfo.Membership.Password = "AReallyStrongPassword";
12 objUserInfo.PortalID = currentPortal.PortalId;
13 objUserInfo.Email = id.Name;
14 UserCreateStatus objUserCreateStatus = UserController.CreateUser(ref objUserInfo);
15 //See if the user was added successfully
16 if (objUserCreateStatus == UserCreateStatus.Success)
17 {
18 //We have created them successfully, so let them into the site
19 LetsLogUserIn(objUserInfo);
20 }
21 else
22 {
23 //This will send the error to the error log, but the user will experience an infinite loop
24 throw new Exception("User not created successfully: " + objUserInfo.Username + "- " + objUserCreateStatus.ToString());
25 }

Here is the LetsLogUserIn function:
view source
01 private void LetsLogUserIn(UserInfo objUserInfo)
02 {
03 try
04 {
05 //Get the current portal
06 PortalSettings currentPortal = PortalController.GetCurrentPortalSettings();
07 //set the language to the default language of the portal
08 Localization.SetLanguage(currentPortal.DefaultLanguage);
09 //Log the user in
10 UserController.UserLogin(currentPortal.PortalId,
11 objUserInfo,
12 currentPortal.PortalName,
13 HttpContext.Current.Request.UserHostAddress,
14 false);
15 }
16 catch(Exception ex)
17 {
18 Exceptions.LogException(ex);
19 }
20 }

We need to make several changes to the web.config. First we need to make the changes necessary for ADFS and then we need to make changes for our HTTPModule.

The ADFS changes are the standard web.config changes you would do for any ADFS claims-aware site. You first need to add the section groups to your web.config.
view source


Then you need to add the actual section. The needs to be EXACTLY what is put into ADFS. Remember, this URL needs to have a / at the end to prevent ADFS from posting to a directory listing. The element needs to be changed to reflect the name of your server.
view source
02 …
05 55
07 https://your_application/
09 https://fs-server/adfs/fs/federationserverservice.asmx
11 …

If you would like to have logging (and who doesn’t like loggingJ) you will need to add the following section at the end of your web.config

More Here


{ 0 comments... read them below or add one }

Post a Comment