[Coding] Web API, AngularJS, and Authentication – Too Many Resources (Part 1)
Posted by Khatharsis on August 16, 2014
I started writing this post a week ago, intending to clean it up and post it sometime this week, but then I got busy. And when I get busy, I get tired and unmotivated/lack the energy to do other things. For some reason, Fridays feel like a pick-me-up. So here I am, cleaning up my original post and separating it out into two posts because the content is heavier than I originally thought.
I was given a small proof-of-concept project to work on involving two new pieces of technology (to me and the software lead): Web API and AngularJS. I have covered AngularJS before. I have had some experience with the MVC Framework when it was back in version 1 and I have muddled my way through more recent versions’ tutorials, so Web API wasn’t a completely new framework to me. Combining the two together, however, is completely new.
One of the specifications of using Web API from the lead, and the point of the proof-of-concept, was if I could secure the Web API from being called anonymously. An initial search resulted in Microsoft’s official articles on using the different forms of authentication with Web API, but they were mostly at a theoretical level and not many practical examples. Further research and asking a knowledgable friend resulted in volumes of articles of practical examples of widely different methods (basic, Forms Authentication, Windows Authentication, oAuth, token, etc.). Too much, actually.
In short, yes, it is possible to require authentication prior to getting any meaningful result from a Web API call. My proof-of-concept went the Forms Authentication route, which my friend said broke the concept of REST (i.e., Web API) because now we’re dealing with state – is the user logged in or not? I went ahead anyway because it was an exercise if nothing else. Turns out the existing application (which would be ideally rewritten using newer technologies like AngularJS, hence the proof-of-concept) also used Forms Authentication so, bonus point. It also revealed to me how lacking my knowledge of authentication methods and even my basic grasp on AngularJS was.
Oh, I forgot to mention, I also dabbled a little bit with the Entity Framework and LINQ in this project. All of these technologies I have been going through tutorials on and getting the theory, but not really the practicality. After all, the most important questions come up when you run into implementation issues on more complex projects than tutorial examples.
My solution file was split into two projects. One was the Web API itself with a simple index.html containing the front-end with AngularJS and a Resources folder holding the CSS and JavaScript files I needed for the front-end. The second was the entity model for the database.
I wanted to have a third project which would be just the front-end, but I quickly found it was going to be difficult wiring it up with the Web API project (as both have to be running to work and I was using IIS Express). Yes, I could tick off the boxes that would make both of them run at the same time, but I also had the code side where I would have to keep track of the correct URL to point to. You can imagine this gets messy between local, QA, staging, and production environments. This first issue I sidestepped because it wasn’t integral to getting the main point of this project across. It’s an architecture question for later.
The project, in a nutshell, was to have a single API call that requires authorization. If unauthorized, the call will return an error. If authorized, the call will return with a simple User object containing the first and last name of a theoretical user.
In this first part, I’ll cover setting up the backend (database/entity model and Web API). In the second part, I’ll cover the front-end/AngularJS bit.
—
Database/Entity model
Since a lot of user authorization on websites is driven in the back by a database, I made a simple User table with an ID, login, and password columns. Then I created an Entity model based on that table. And that was all I really had to do on the far backend.
This process ideally mirrors what would take place with the rewrite of the original application. The database structure is already in place, so I would be taking a database-first approach to the entity models.
Web API
For the Web API, I created two controller classes: a User and an Auth (for authentication). The User controller class was decorated with [Authorize] and had one working method, GetUsers(), defined as:
public IEnumerable<Db.User> GetAllUsers()
{
var query = from user in db.Users
select user;
return query;
}
Which is, just a LINQ query returning an array of User entity models.
The Auth class should be able to be accessed anonymously. It makes no sense for a user to be logged in when he needs to log in. (I have had a few of these circular experiences around my work lately where A requires B which requires A.. which is why I bring this up.) I have three methods, SignIn(), SignOut(), and IsAuthenticated(). The first two methods are self-explanatory, but the third method I was fiddling around with the front-end and needing to display a login form and a sign out button/link depending on the state of the user. This method also comes in handy when a signed-in user refreshes the page.
The most helpful bits of the SignIn() method is as follows:
[HttpPost]
public HttpResponseMessage SignIn([FromBody]Db.User u)
{
// Authentication logic has been snipped, but basically it returns a record or not,
// which ultimately sets a result variable:
if (result != null)
{
var response = this.Request.CreateResponse(HttpStatusCode.Created, true);
FormsAuthentication.SetAuthCookie(result.Username, false);
return response;
}
else
{
var response = this.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "");
return response;
}
}
All I am doing is creating the appropriate response object (successful) and setting the Forms Authentication cookie. If authentication fails (i.e., no user/pass combo is found), then an error response is returned instead. Notice the HttpPost decorator. Since I am “creating” something (the cookie), it made sense for this to be a POST operation.
Also notice the argument. I am not passing arguments via the URL, but through a POST package request. [FromBody] will pull out the POST data and automatically map it to my user entity model. The property name in the JSON should match the property name in the entity model.
Likewise, with the SignOut() method:
[HttpPost]
public HttpResponseMessage SignOut()
{
var response = this.Request.CreateResponse(HttpStatusCode.Created, true);
FormsAuthentication.SignOut();
return response;
}
Basically a mirror of the SignIn() method. I create a success response and instruct the server to delete the cookie. Again, since I am “deleting” something, I decorate the method with HttpPost. It’s probably also viable to use DELETE instead of POST. But, since I’m violating the principles of REST by using cookies anyway…
And finally, the IsAuthenticated():
[HttpGet]
public IHttpActionResult GetIsAuthenticated()
{
if (System.Web.HttpContext.Current.User != null &&
System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
{
return Ok(true);
}
return Ok(false);
}
A valid result will always return some boolean value.
The last bit of the Web API back-end involves a new route configuration for the authentication (just the relevant bit here):
routeTemplate: "api/{controller}/{action}"
I did try "api/auth/{action}", but ended up with a 404 error (No HTTP resource/route providing a controller name was found to match request URI). That’s something else for me to investigate later. Another error(s) I ran into was “Multiple actions were found that match the request”. After trying to assign an ActionName and trying route prefixing, separately and together (essentially throwing any and all solutions I found at it) and getting even more obscure-ish errors, it turns out the solution was to move the route call above the DefaultApi route. Logically, this makes sense. ASP.NET (and probably any human who isn’t familiar with the code) can’t easily tell the difference between an action and an id. I’m still frustrated that the top results from searching point to solutions that didn’t work.
By this point, I was itching to test if my methods were working. Having finally worked with unit and integration tests this past year, I felt uncomfortable writing this much code and not having a single test to test against. But, proof-of-concept. So, old fashioned testing and on to the front-end.