Friday, July 16, 2010

Authenticating Against Multiple Domains with C# & StructureMap




In my place of work, we had a vendor come in and try to sell us a product that couldn’t authenticate to multiple domains. Well, we have multiple domains. Their suggestion was to consolidate into one…obviously we’re not using their product. So, in an effort to help out, I thought I’d share a method of authenticating to multiple domains.

As always, we start out with our interface…

  public interface IAuthenticationService
{
bool Validate(string username, string password);
string DefaultRedirectUrl { get; }
}

Pretty straightforward so far…I added in a DefaultRedirectUrl in case you want to route to different places depending on which domain you authenticated against.

Okay, let’s implement our interface…

    public class AuthenticationService : IAuthenticationService
{
private readonly IAuthenticationService[] _authenticators;
private string _redirecturl;

public AuthenticationService(IAuthenticationService[] authenticators)
{
_authenticators = authenticators;
}

public bool Validate(string username, string password)
{
var isValid = false;
foreach (var authenticator in _authenticators)
{
if (authenticator.Validate(username, password))
{
isValid =
true;
_redirecturl = authenticator.DefaultRedirectURL;
break;
}
}
return isValid;
}

public string DefaultRedirectUrl
{
get { return _redirecturl; }
}
}

Alrighty…so all this does is takes in an array of authentication services and then loops through them checking if they’re valid and if they are, sets isvalid to true, the redirect url, and returns.

Basically this is our master authentication service. Now, we just need one for each one of our domains. Since they’re both authenticating to AD, we’ll create a base class like this:

    public abstract class ActiveDirectoryAuthenticator : IAuthenticationService
{
private readonly ILDAP _ldap;

protected ActiveDirectoryAuthenticator(ILDAP ldap)
{
_ldap = ldap;
}

public bool Validate(string username, string password)
{
var authenticated = false;
try
{
var entry = new DirectoryEntry(_ldap.URL, username + "@" + _ldap.HostName, password);
object nativeObject = entry.NativeObject;
authenticated =
true;
}
catch (DirectoryServicesCOMException)
{
}
return authenticated;
}

public virtual string DefaultRedirectUrl
{
get { return "/"; }
}
}

So, my ILDAP interface is the same one I blogged about here. The DefaultRedirectUrl here is virtual and meant to be overridden, otherwise it’ll just redirect to the root. Okay so now we need to inherit this authenticator in our domain authentication service classes like this…

Domain #1

public class Domain1AuthenticationService : ActiveDirectoryAuthenticator
{
public Domain1AuthenticationService() : base(DependencyRegistrar.Resolve<ILDAP>(DependencyNames.Domain1LDAP))
{ }

public override string DefaultRedirectUrl
{
get
{
return "/domain1/default.aspx";
}
}
}

Domain #2

public class Domain2AuthenticationService : ActiveDirectoryAuthenticator
{
public Domain2AuthenticationService() : base(DependencyRegistrar.Resolve<ILDAP>(DependencyNames.Domain2LDAP))
{ }

public override string DefaultRedirectUrl
{
get
{
return "/domain2/default.aspx";
}
}
}

Pretty simple right? Basically we’re setting our RedirectUrl and calling our ILDAP from StructureMap. You can read more about how I implement StructureMap here. Now for the good ole StructureMap code that goes in the Registry…

ForRequestedType<ILDAP>()
.AddInstances(x =>
{
x.OfConcreteType<
LDAP>()
.WithName(
DependencyNames.Domain1LDAP)
.SetProperty(y => y.WithBaseDN(ou =>
"Users", dc => "dc1", dc => "domain", dc => "com")
.WithHost(
"dc1.domain.com"));

x.OfConcreteType<
LDAP>()
.WithName(
DependencyNames.Domain2LDAP)
.SetProperty(y => y.WithBaseDN(ou =>
"Users", dc => "dc2", dc => "domain", dc => "com")
.WithHost(
"dc2.domain.com"));
});

ForRequestedType<
IAuthenticationService>()
.TheDefault.Is.OfConcreteType<
AuthenticationService>()
.TheArrayOf<
IAuthenticationService>().Contains(x =>
{
x.OfConcreteType<
Domain1AuthenticationService>();
x.OfConcreteType<
Domain2AuthenticationService>();
});

So, how to use this in your code? Like this…

 var adService = DependencyRegistrar.Resolve<IAuthenticationService>();

if (adService.Validate(tbUsername.Text, tbPassword.Text))
{
FormsAuthentication.SetAuthCookie(tbUsername.Text, true /*persist cookie*/);

if (Request.QueryString["ReturnUrl"] == null)
Response.Redirect(adService.DefaultRedirectUrl);
else
Response.Redirect(Request.QueryString["ReturnUrl"]);
}
else
//Display Error

There it is…authenticating against multiple domains. Please let me know if you know of better ways or you see ways to improve. Thanks for reading!

Shout it

kick it on DotNetKicks.com

blog comments powered by Disqus
Related Posts Plugin for WordPress, Blogger...