Updated on 8/12/2008 — It has come to my attention this solution stopped working with DotNetNuke 4.8. I believe it should work with 4.5 - 4.7. I don’t work with DNN anymore so I probably can’t be very helpful, but if someone else has written a newer post on the topic, I’d be happy to link there.
—
So I’ve been working with DotNetNuke as our portal solution at work. My first task was to plug in to the authentication scheme to make it use our existing user database. This seems like a simple request, and in the end it was.
My big problem was the lack of documentation. First of all there is very little experience out there with DNN4. Secondly, the other providers of SSO are either commercial or very confusing, especially for a Java-turned-C# developer like myself. So I thought I’d lay it out, because it’s not very hard.
First of all, I didn’t want to completely gut the entire system, because DNN provides a lot of nice role management stuff interally. So what I did was to write a go-between that transparantly creates DNN accounts if you can authenticate to our existing user database. I wrote a new Membership Provider that extends the AspNetMembershipProvider. Heres some code:
-
class MembershipProvider : AspNetMembershipProvider
-
{
-
public MembershipProvider(): base(){}
-
-
override public UserInfo UserLogin(int portalId, string username,
-
string password, string verificationCode,
-
ref UserLoginStatus loginStatus)
-
{
-
if (password == “!EXTERNAL_AUTH!”)
-
{ //protect the reserved word
-
loginStatus = UserLoginStatus.LOGIN_FAILURE;
-
return null;
-
}
-
-
//Ok first up if you can log into the portal that’s good enough for me
-
UserInfo dnnUser = base.UserLogin(portalId, username, password,
-
verificationCode, ref loginStatus);
-
log.Debug(“DNN Login status = “ + loginStatus.ToString() );
-
if ( loginStatus == UserLoginStatus.LOGIN_SUCCESS ||
-
loginStatus == UserLoginStatus.LOGIN_SUPERUSER )
-
return dnnUser;
-
-
//This user doesn’t exist in DNN
-
User u = null;
-
try
-
{
-
u = Internal.Login.Function(session, username, password);
-
}
-
catch (Exception ex)
-
{
-
log.Info(ex.StackTrace);
-
loginStatus = UserLoginStatus.LOGIN_FAILURE;
-
return null; //I hope this works.
-
}
-
-
//If we get here with no exception, the user is valid in the external database
-
//So try to log them in using the fake password
-
dnnUser = base.UserLogin(portalId, username, “!EXTERNAL_AUTH!”,
-
verificationCode, ref loginStatus);
-
if ( loginStatus == UserLoginStatus.LOGIN_SUCCESS ||
-
loginStatus == UserLoginStatus.LOGIN_SUPERUSER )
-
return dnnUser;
-
-
//Not in the DB, make a new user since they are authenticated
-
newUser.DisplayName = “New User”;
-
newUser.Email = “email”;
-
newUser.PortalID = portalId;
-
newUser.Username = username;
-
newUser.IsSuperUser = false;
-
//This is an important part to be sure the user has our special password
-
um.Password = “!EXTERNAL_AUTH!”;
-
um.Approved = true;
-
um.Username = username;
-
newUser.Membership = um;
-
base.CreateUser(ref newUser);
-
newUser.FirstName = “Unknown”;
-
newUser.LastName = “Unknown”;
-
base.UpdateUser(newUser);
-
//Try to log them in again
-
dnnUser = base.UserLogin(portalId, username, “!EXTERNAL_AUTH!”,
-
verificationCode, ref loginStatus);
-
if (loginStatus == UserLoginStatus.LOGIN_SUCCESS ||
-
loginStatus == UserLoginStatus.LOGIN_SUPERUSER)
-
return dnnUser;
-
-
}
-
}
Then change the web.config to use your new MembershipProvider instead of the AspNetMembershipProvider. That’s really all there is to it.
It probably also wouldn’t be a bad idea to override some of the other functions like ChangePassword, DeleteUser, CreateUser, etc. depending on your situation.
Nice and simple! Is this still working well for you? I am about to do the same thing and would appreciate your current thinking on the above approach.
Comment by neilx — April 3, 2008 @ 1:59 pm
This is still working on our production site, but it appears that this method no longer works in DotNetNuke 4.8. Our site is still running 4.5, and I believe this solution will work on 4.6 as well. I didn’t look very long to see what caused my solution to stop working after the upgrade, but it looked like they may have changed the way authentication worked.
If you find out anything useful please let me know, and I will post again if I solve the problem.
Comment by Brian Samson — April 3, 2008 @ 5:45 pm
I am trying this now. Could you clarify line 27 for me please?
27. u = Internal.Login.Function(session, username, password);
Neither Internal nor session compile for me.
I also assume you have a log class created somewhere that isn’t part of the dnn install? For example line 18:
log.Debug(“DNN Login status = “ + loginStatus.ToString() );
Comment by neilx — April 7, 2008 @ 10:57 am
Whoops. I guess line 27 simply authenticates in you own db. Sorry (blushes uncontrollably:-)
Comment by neilx — April 7, 2008 @ 11:00 am
Correct on both accounts. We use log4net as our logger which was set up earlier or perhaps globally.
Comment by Brian Samson — April 7, 2008 @ 11:09 am
Update - I couldn’t get it to work either so I have now made the ValidateUser() method in AspNetMembershipProvider protected overridable and I am overriding that method instead. I can’t imagine why it is almost the only not overridable method in there. So far so good, but it is early days yet:-)
Comment by neilx — April 11, 2008 @ 1:29 am
Brian - I have bitten the bullet and created a decorator for AspNetMembershipProvider so I don’t need to modify core code. This is working fine under test. I stole some of your code above for creating a new user - thanks:-)
I realised that I will need to do this a lot, even having multiple methods for authentication for some clients, so I think the extra work needed to create a Decorator Pattern approach will be worth it in the long run for me. You are welcome to see the code of course, although it purely in proof of concept form at the moment.
Comment by neilx — April 14, 2008 @ 1:25 am
Brian, I was wondering if you knew how to package this up and how to install it? I’ve taken the .dnn file from the DNN source and tried that (using the Authentication install option under HostPortal settings) and it says it installs but doesn’t copy the dll into the bin folder.
cheers,
Stephen
Comment by Lyynx — August 12, 2008 @ 2:01 am
Hello Lyynx.
For something like this, I usually just put the .dll in the bin directory on the web server and then change the web.config to use my new classes.
Please note as well that this post is out of date and no longer works (as far as I know) with DotNetNuke 4.8 or higher. I’m not aware of an Authentication install option, so this may well be part of their new, better pluggable authentication system. Sorry.
Comment by Brian Samson — August 12, 2008 @ 10:56 am
I am trying this now.
- Could you provide the example of changing the web.config to use your new MembershipProvider instead of the AspNetMembershipProvider, please ?
I’m getting stuck on this.
-Thanks
Comment by monysimkh — January 8, 2009 @ 5:57 pm
It also works with DNN 5.2 But this version uses another overload of the method with the parameter authType:
public override UserInfo UserLogin(int portalId, string username, string password, string authType, string verificationCode, ref UserLoginStatus loginStatus)
Comment by Stefan Z — March 23, 2010 @ 3:15 am