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

Related Posts Plugin for WordPress, Blogger...