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!