Pages

Integrate DNN with ADFS without writing a new Authentication Provider

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.
HTTPModule


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
print?
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
print?
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
print?
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
print?
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
print?
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
print?
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 }
Web.Config


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
print?
1
2

3


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
print?
01
02 …
03
04
05 55
06
07 https://your_application/
08
09 https://fs-server/adfs/fs/federationserverservice.asmx
10
11 …
12


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


Courtesy:http://binnington.wordpress.com/

2 comments:

  1. Hi Michael,

    Seems like I won the lottery here…. This is a treasure box of blogs and your folks are like leprechauns! Phenomenal read on Integrate DNN with ADFS without writing a new Authentication Provider!


    I don't plan on using Linux as a primary operating system, since I don't really need to. But Linux offers software knowledge too. I'm curious to try something out with it.
    File System is a method to store and organize files and directories on disk. A file system can have 2 different formats called file system types. These formats determine how the information is stored as files and directories.
    Very useful article, if I run into challenges along the way, I will share them here.

    Thanks,
    Kevin

    ReplyDelete