Adding an MVC layer on top of a Web API backend

Reading Time: 8 minutes

It might just be me, but I don’t seem to find a lot of examples out there showing how you can have an ASP.NET MVC website as a front end application using a Web API project as the backend service. Especially so when your front end is as basic as possible: I don’t want to end up storing user data twice because I need to request OAuth tokens and store refresh tokens and so on…

If you want to dive into the code that I’ve produced, you can head straight to GitHub and fetch it 🙂
For more explanation, read on.

OK, suppose I want to have a Web API on one server and a MVC front end on another server: how do we authenticate our users then, and how do we remember that authentication when accessing the Web API? Let’s start by configuring the Web API part.

Web API Configuration

In the WebApiConfig class, I’ve set up two HostAuthenticationFilter’s. You can specify as much of these filters as you want, each time providing another authentication type.

  • The first one handles OAuth authentication requests, using a “Bearer” token. This means that in an HTTP request, you’ll add an Authorization header with the value “Bearer <auth token here>”
  • The second one handles cookie authentication requests. Because I’ve used the DefaultAuthenticationTypes.ApplicationCookie value, I know that it will match the settings that I’m going to define in the Startup.Auth.cs file.

For this to work, you also need to set up the correct authentication middleware. Adding these filters just tries to authenticate, but if no handler is found for the authentication type, nothing will happen. So let’s go to Startup.Auth.cs and configure these handlers.

To handle cookie authentication, I’ve added the app.UseCookieAuthentication call. When generating a new Web API project, this line is also added but with the default CookieAuthenticationOptions. In this case, I want to have a bit more control over what actually happens, so we’ll change the configuration slightly:

  • AuthenticationType is set to DefaultAuthenticationTypes.ApplicationCookie. This should match the authentication type used in the HostAuthenticationFilter. Note: the default value is set in the constructor to CookieAuthenticationDefaults.AuthenticationType, which eventually is a string “Cookies”.
    And if you really want to, you can specify your own name here. Just make sure to repeat the same name when you would otherwise use ApplicationCookie.
  • CookieHttpOnly is set to false, because I want to be able to authenticate using cookies from JavaScript as well.
  • Actually, Aymiee Lee pointed out that HttpOnly should NOT be false in this case. When performing AJAX requests in JavaScript, cookies will be sent along with those requests even if they are HTTP only. You only need to set this to false if you need to read cookies inside JavaScript, but that makes your website vulnerable to XSS attacks.
  • And finally, I’ve set a specific CookieName, so I know which name I need to use everywhere to read or write the cookie.

The second handler (for bearer tokens) is also generated by default and I didn’t change it, except the #if DEBUG line around AllowInsecureHttp. Never, ever allow insecure HTTP calls in production environments!

Just one more thing: in the web.config file, I’ve added a machineKey setting in the system.web section. This will need to be the same in our MVC site, otherwise the Web API layer can’t read the cookie we’ve created in the MVC site.

MVC Site Configuration

Head over to web.config to see that I’ve indeed added the same machineKey in here. I’ve also added an appSetting called WebApiUri which holds the base uri where the API lives. If the Web API project runs on a different port on your machine, you’ll need to change that port here.

Lastly, authentication has been configured to use Forms Authentication, using the /Account/SignIn action to authenticate our users.

Registering

First of all, if you want to test the application, you’ll need to register yourself. So after running both the Web API and the MVC projects, head over to the MVC site and click on the Register link in the top right.

Register

Fill in the details and click the Register button. This might take a while, because Entity Framework needs to create the database first. Now, you can sign in using the just entered email address and password.

Registering is easy, because that can be done with a call to the Web API without having to be authenticated. If you go to the AccountController in the MVC project, check out the Register action method:

Using the WebApiService class (a simple wrapper for HttpClient), we post the RegisterModel to the Web API. After checking the data, it will then either return a BadRequest, or OK if the user profile has been created.

Signing in

After checking the ModelState, you can ask the Web API if it knows the given user credentials and of course if they are valid. To do this, you can make use of the “/Token” endpoint path, as configured in the Startup.Auth.cs file. Again, I’ve wrapped this in the WebApiService class:

The “/Token” endpoint expects form-encoded data instead of XML or JSON, that’s why I had to use the FormUrlEncodedContent class to specify the data passed to the PostAsync method. The form body contains three parameters, of which the grant_type could need some explanation.
There are different grant_type‘s possible:

  • password – used to authenticate using a userName parameter and a password parameter, as I’m doing here.
  • authorization_code – used to authenticate using a client id and secret.
  • client_credentials – used when a client is requesting access to protected resources under its control (from within the Web API itself). Not usable in this case.
  • refresh_token – used to refresh an access token if a refresh_token was given (most likely when requesting access with the authorization_code grant type)

If you’re wondering how this authentication is done, have a look inside the ApplicationOAuthProvider class in the Web API project. This class is also being generated when you create a new Web API project and specify that you want to have individual user accounts. The GrantResourceOwnerCredentials method is the one handling the authentication request.

After authenticating, you’ll get a SignInResult containing these fields:

  • access_token – the magical identifier you can use in the Authorization header. This is your “Bearer” token.
  • token_type – the type of token you’ve received. Currently, ASP.NET only returns “bearer”.
  • expires_in – number of seconds until this token expires
  • userName – the user name as known in Web API
  • .issued – the UTC date/time when this token has been granted
  • .expires – the UTC date/time when this token expires

With this information, we can now keep our user authenticated in both the MVC site and the Web API.
For MVC, we use the FormsAuthentication.SetAuthCookie method to set the User.Identity and create the ASPXAUTH cookie. Note that I’m using the AccessToken as user name, you’ll see why I’m doing that in just a minute.

But I also want to set another cookie, so I can authenticate in JavaScript. Remember that I’ve configured Web API to allow logging on using the ApplicationCookie? Now we’re going to create it.

The code above creates a new ClaimsIdentity with its authenticationType set to ApplicationCookie. This needs to match the HostAuthenticationFilter in Web API, or else it will not pick up this cookie to authenticate. That ClaimsIdentity is then being stored in an AuthenticationTicket because Web API will try to deserialize the cookie into this type. It doesn’t stop here though, because before we can save the cookie, we need to convert the AuthenticationTicket into a base64-encoded string. So the ticket is being serialized, encrypted (and now you see why we need the same machine keys for MVC and Web API) and base64 encoded.
Finally, we can create a new cookie with the same name as configured in Web API. Again, HttpOnly is false to enable using this cookie in JavaScript.

Accessing protected resources

In MVC, head over to the TodoController and look at the TestApi action method first.

When calling GetAsync or PostAsync on the WebApiService, you can add another parameter containing your authToken. Because we’ve specified this to be our user name when calling SetAuthCookie, this value is stored in User.Identity.Name in MVC. Inside the GetAsync method, I’m adding another header if this authToken has been set:

But how do you access the Web API using JavaScript? Well, you could output the authToken in your view inside a script and pass a header object in your AJAX requests:

Or, if you’re like me and want to do something special, you can use a cookie. In todoItems.js, I’ve changed the default ajax settings to include all cookies when making AJAX requests:

But when you start to use JavaScript to access an API that is hosted on another port (or domain), you’ll immediately get errors when trying to access the API. You’ll have to enable cross-origin shared resources (CORS) for this to work. In the WebApiConfig class, the line config.EnableCors enables CORS support for Web API (provided by the Microsoft.AspNet.WebApi.Cors NuGet package), but you also need to mark controllers or actions with the EnableCorsAttribute to actually allow accessing these resources.

In the TodoItemController in Web API, I’ve added this attribute to allow access from the MVC url. Again, if these projects are running on a different port, you’ll need to change this. You can use * to allow any origin. The second parameter limits the headers (* for any), the third limits the operations (GET, POST, and again * to allow any). The last parameter is used so we can pass cookies in the AJAX request.

And that’s it. Now you can use the ApplicationCookie in JavaScript to authenticate against Web API without having to explicitly add an auth token or a cookie name in your JavaScript code.

 

 

  • Pingback: Wesley Cabus | Cors.ConfigProfiles is here!()

  • Stuart

    Thanks for this, a good article and was something I searched for quite a while on until I found this.

    One last challenge I face is getting the existing Claims from the WebApi Authentication call as I would like to lock down certain sections of the MVC app based on the users claims using a custom implementation of the AuthorizeAttribute. Can I create an ClaimsIdentity from the Bearer token as this has all the information that I need in?