DotNetNuke 4 Custom Authentication / Single Sign On
Posted: July 27, 2006 Filed under: .NET, Computers, DotNetNuke 11 Comments »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:
[csharp]
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
UserInfo newUser = new UserInfo();
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
UserMembership um = new UserMembership();
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;
throw new Exception(“FATAL: Unknown error mapping external login to DNN”);
}
}
[/csharp]
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.
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.
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() );
Whoops. I guess line 27 simply authenticates in you own db. Sorry (blushes uncontrollably:-)
Correct on both accounts. We use log4net as our logger which was set up earlier or perhaps globally.
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:-)
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.
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
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.
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
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)