Sunday, February 28, 2010

Authentication with Active Directory, ASP.NET MVC, & fluentHtml




I figure since people are starting to really look into MVC and are realizing that it’s going to be around for a while, they’re going to need some decent samples when researching how to do things. I’m hoping this particular post will possibly be one of those examples. I had the same thought when I wrote the Security Questions with ASP.NET MVC & fluentHtml post.

Let’s get started with the view model class for the sign in page. By the way, if you’re not familiar with the way I setup my MVC projects, see this post.

public class SignInView
{
[
ValidateNonEmpty("Username is required.")]
public string Username { get; set; }
[
ValidateNonEmpty("Password is required.")]
public string Password { get; set; }
}

Really simple. The ValidateNonEmpty is an attribute from the Castle project and I’m passing in the error message I want displayed if invalid.

Okay, so let’s go ahead and pass in this view model to our actual view from the AuthenticationController. This will look like this:

public ActionResult Index()
{
return View(new SignInView());
}

Now we’ll create our actual view (html):

<%=Html.DivValidationSummary("All fields are required.") %>
<form action="/authentication/index" method="post">
<
fieldset>
<
legend>Sign In</legend>
<
p><%=this.TextBox(f => f.Username).Label("Username: ")%></p>
<
p><%=this.Password(f => f.Password).Label("Password: ")%></p>
<%=Html.AntiForgeryToken() %>
<%=Html.SubmitButton("Sign In", cssclass=>"sb", accesskey=>"S") %>
</
fieldset>
</
form>

This is our basic html setup with fluentHtml added to it. If you need a tutorial on fluentHtml, here’s a post on it. Basically, I’m using my htmlHelper for a DivValidationSummary, I’m calling the index action on the authentication controller, then I’m setting up my textboxes with fluentHtml, using the antiforgerytoken to prevent csrf, and I’m using my submit button that I blogged about before this post.

Now let’s look at the action that’s being called by the form above.

[AcceptVerbs(HttpVerbs.Post), ValidateModel(typeof(SignInView)), ValidateAntiForgeryToken]
public ViewResult Index(SignInView form)
{
if (!ModelState.IsValid)
return View("index", form);

IUser user = DependencyRegistrar.With<string, IUser>(form.Username);
bool passwordMatches = _authenticationService.PasswordMatches(user, form.Password);

if (passwordMatches)
{
//Sign In – Write Cookie & Redirect most likely through a UserSession
}

ModelState.AddModelError(
"SignIn", "Invalid credentials");
return View("index", form);
}

Okay, so you can see I’m accepting only post, I’m validating the model, and I’m validating the token. I’m also accepting my SignInView model. If the model state is not valid I return the model back to the view. Otherwise, I get an instance of user with my username and then I call my authenticationService. If passwordMatches, sign in and return, otherwise add an error and return.

So let’s look at my IUser real quick. It’s really simple:

public interface IUser
{
string Username { get; set; }
}

Think my AuthenticationService is much more difficult? Of course not!

public interface IAuthenticationService
{
bool PasswordMatches(IUser user, string password);
}

By the way, I’m accepting an instance of IAuthenticationService in the constructor of the controller like so:

public AuthenticationController(IAuthenticationService authenticationService)
{
_authenticationService = authenticationService;
}

I’m using StructureMap to know what to pass in to the constructor. Okay, so my AuthenticationService implementation looks like this:

public class AuthenticationService : IAuthenticationService
{
private readonly IActiveDirectorySettings _adSettings;

public AuthenticationService(IActiveDirectorySettings activeDirectorySettings)
{
_adSettings = activeDirectorySettings;
}

public bool PasswordMatches(IUser user, string password)
{
var authenticated = false;
var domainAndUsername = _adSettings.DomainName + "\\" + user.Username;
var entry = new DirectoryEntry("LDAP://" + _adSettings.LdapPath, domainAndUsername, password);

try
{
var bindToNativeObjectToForceAuthentication = entry.NativeObject;
authenticated =
true;
}
catch (COMException)
{
}

return authenticated;
}
}

Now my IActiveDirectoryCredentials looks like this:

public interface IActiveDirectorySettings
{
string DomainName { get; }
string LdapPath { get; }
}

That’s basically it. There’s not a whole lot to an authentication process, but thought I’d share anyhow.

Thanks for reading!


Shout it

kick it on DotNetKicks.com

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