17
votes

I have a Mvc 5 client that have a Ownin cookie authentication. I also have a Web Api that is protected with Owin Bearer token (I used the VS2013 Web Api template, that create the Token endpoint)

Ok, now my Mvc 5 client need to use my WebApi. I created a method to get the bearer token:

internal async Task<string> GetBearerToken(string siteUrl, string Username, string Password)
{
     HttpClient client = new HttpClient();

     client.BaseAddress = new Uri(siteUrl);
     client.DefaultRequestHeaders.Accept.Clear();

     HttpContent requestContent = new StringContent("grant_type=password&username=" + Username + "&password=" + Password, Encoding.UTF8, "application/x-www-form-urlencoded");

     HttpResponseMessage responseMessage = await client.PostAsync("Token", requestContent);

     if (responseMessage.IsSuccessStatusCode)
     {
         TokenResponseModel response = await responseMessage.Content.ReadAsAsync<TokenResponseModel>();
         return response.AccessToken;
     }

     return "";
}

And in my Mvc action I called that:

public async Task<ActionResult> Index()
{
     var token = await GetBearerToken("http://localhost:6144/", "teste", "123456");

     using (var client = new HttpClient())
     {
         client.DefaultRequestHeaders.Add("Authorization", "Bearer "+ token);

         var response = await client.GetAsync("http://localhost:6144/api/values");

         if (response.IsSuccessStatusCode)
         {
             var data = response.Content.ReadAsAsync<IEnumerable<string>>();

             return Json(data.Result, JsonRequestBehavior.AllowGet);
         }
     }   
}

That all works fine... But I need to use the Web Api in all my actions... So how can I keep that token (despite getting a new token every request) and how verify if it expired ... Is it possible to keep that together with authentication cookie someway? Any best pratices to deal with that scenario?

Thanks

3
Just a remark here, when you post to /token the response contains an access_token usable as a bearer authentication on Web API calls, but it also returns a set-cookie header which automatically saves a cookie locally when called from $.ajax, and this cookie will authorize MVC calls. So 1 login action is enough for both controllers (mvc and webapi), I save the access_token in localStorage for web api calls, personally;Felype

3 Answers

16
votes

If I get it right your MVC 5 client app is accessing a WebAPI of a different app. The MVC 5 Client uses a cookie to authenticate the user. To access the WebAPI you get a Bearer tokeen from the /Token endpoint and send it in the Authorization header.

You do not call the WebAPI from your client side Javascript code, you just call it from within MVC Actions running on the server of the MVC5 application.

Getting a new Token before each service call sounds wrong. This would mean 2 roundtrips each time. This can't be performant.

If I got that right, you could:

  1. Store the token in the Session object. For as long as your user of the MVC App is authenticated and his session is alive you would then always have the same Token. If its expired you would get a 401 unauthorized access back from the WebAPI. To keep your MVC Action Unit Testable you could wrap the session access into a Service that you inject into the Action (Dependency Injection).

  2. you could store the Token in a cookie similar to the Authentication cookie already existing. This way you would not need a Session on the server side. Here again I would wrap the access to get the Token from the Cookie in a service that all your actions use.

I would use the Session storage. Simple. Straight forward. But maybe I am missing something

Hope this helps you. Feedback appreciated :-)

4
votes

Bearer token is not a good way to authorize your web application. If you store services' token in cookie it will be available to the application's clients, so service layer will be vulnerable to application's clients. The only solution seems to be keep token in a session but you will lose stateless nature of your application.

Here is describied what/how bearer token should be used: "A bearer token is just a big, random string that a client must present on every API call. Bearer tokens are simple because there's no special signature or validation code required on either end. The client is responsible for storing the token in a safe place and sending it with every request. The server is responsible for looking up the token in a database and making sure it's a valid one -- that's it.".

Here is good example of using bearer token in single page application where client directly talks to the service.

Anyway I would suggest you to use HMAC authentication, BCrypt or ClientCertificates. Even amazon uses it for authenticating REST requests.

2
votes

If you want to manage the tokens across all of your actions, you should change the code to use a custom authorization filter. That filter can be added to all Web API requests, all actions for a controller, or an individual action. To do that, derive from the AuthorizeAttribute and issue the GetBearerToken call from the filter. Stick the token into the HTTP context for usage during request processing. Instead of directly calling creating HttpClient instances, you could use a factory to generate them and add the appropriate tokens for authentication.

As for determining if the tokens are expired, you could add an additional filter that checks for specific errors coming back or alternative issue a check in the authorization filter. I don't know all of your requirements so it's difficult to determine the appropriate solution there.