This is part of a somewhat broader topic of unit testing that I will probably cover more in depth later.
Unit Testing Authentication on a Web API Controller
At work I made a Web API Controller that has a POST method on it to allow someone to forcibly suspend another user’s session. This kind of action is obviously limited to people who have the proper credentials, so I wanted to make sure I was checking to see if they are authenticated on the web server before the action could be completed.
My controller action looked something like this:
[code language=”csharp”]
[HttpPost, Authorize, Route("ForceSuspend/{userId:long}")]
public async Task ForceSuspend(long userId)
{
if (!User.Identity.IsAuthenticated)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Unauthorized,
"You are not authorized to force suspend anyone!"));
}
await SessionService.ForceSuspend(userId, User.Identity.Name);
}
[/code]
Since authentication is taken care of by ASP.NET, I can simply double check to make sure that the user is authorized. The User property of the HttpConnectionContext is just a handy-dandy prepopulated IPrincipal that can be used for all of your authorization needs. Using this you can check to make sure a user is authenticated, get their name, and check to see if the user is part of a security role. For my purposes right now, I just want to double check and make sure that the user is authenticated. If they aren’t authenticated, the name I get back from User.Identity.Name will be null, or, even worse, Identity might be null, throwing a null reference exception.
When I started writing unit tests for this particular call, I wanted to make sure I tested the case of a user not being authenticated. Theoretically this should never happen, but I could say that about tons of other bugs I’ve written.
I’m using a mocking framework called Moq, which makes everything really easy to use. I especially like it because it works really well with Autofac, which I’m using for dependency injection. With their powers combined, unit testing isn’t even that hard anymore!
The thing that took me the longest to figure out is how to force myself to be unauthenticated. It seemed like unit tests really want to just be authenticated by default. It took me awhile to come up with this code, but it really makes perfect sense now that I stop and look at it.
[code language=”csharp”]
[TestMethod]
public async Task ForceSuspend_NotAuthenticated_ThrowsUnauthorizedException()
{
using (var mock = AutoMock.GetLoose())
{
long expectedUserId = 123414;
var user = mock.Mock<IPrincipal>();
user.SetupGet(u => u.Identity.IsAuthenticated).Returns(false);
var controller = mock.Create<SessionApiController>();
controller.Request = new System.Net.Http.HttpRequestMessage();
controller.User = user.Object;
try
{
await controller.ForceSuspend(expectedUserId);
Assert.Fail("No Exception was thrown.");
}
catch (HttpResponseException ex)
{
Assert.AreEqual(HttpStatusCode.Unauthorized, ex.Response.StatusCode);
}
catch
{
Assert.Fail("HttpResponseException was not thrown.");
}
}
}
[/code]
By mocking IPrincipal, we can easily set the properties to be whatever we want. Even if the unit test framework for some reason keeps authenticating the user as me (when I’m running the tests as my machine), I can easily tell the test to be someone else, or, like in this case, tell the test that I’m not actually authenticated. We just have to set the mocked object equal to the controller’s User property and we’re golden!
Not the hardest thing to understand, but it took me long enough to figure out the first time that I thought I would write it down to save myself (or someone else) the hassle for next time.