11
votes

I have a site utilizing both mvc and web api controllers and aspnet identity. I used the VS2013 SPA template with both mvc and web api controllers as a starting point.

Here is my scenario:

User logs in using mvc controller and gets back an auth cookie.

Next page is served using using mvc controller that is authenticated. That page uses knockout and does a ajax post call to a web api controller that is authenticated from a button click (Save). The web api controller requires the Authentication header with Bearer --token--.

My question is how do others handle this "two" authentication approaches. I created an endpoint on the mvc controller that is authenticated and returns a bearer token based on the current principal. I can use that token to post to the web api endpoint successfully.

The javascript logic I'm creating is a bit...confusing. It checks to see if a access token is in session storage, if so make the ajax call. If not, call the get token endpoint and then call the web api endpoint (using a bunch of callbacks for handling Ajax promise done, fail, ect).

How have others handled the scenario where you need both the auth cookie and bearer token so each "mvc" page is authenticated, and the web api endpoint that page calls is authenticated. What do you do if the bearer token expires before the cookie expires.

Let me know if I'm not clear or if you need more information.

Edit

I came across this, Using bearer tokens and cookie authentication together It still doesn't answer my question as I have it already setup so mvc accepts cookie auth and web api only accepts bearer token. I feel like this should be a problem that is already solved, but maybe I'm taking the wrong approach.

2
How did you manage to get around this? I did exactly what you described for a past project but I am just starting a new one and I was interested in knowing if there are any "best practices" around this I can implement this time aroundObi
I got around it by doing all the ajax calls to the mvc controllers. Trying to juggle both cookie based token and bearer token was too complicated. Our website used only mvc controllers, our outside world used web api. I kept most of the "reusable logic" in a common business layer. I'm still very interested in seeing what approach others have taken.kheit

2 Answers

3
votes

I think you basically stumbled across the core problem with OAuth2.0. OAuth2.0 is only an authorization protocol. What you need is a security model that supports both authentication and authorization.

Introducing OpenId Connect

OpenId Connect is a authentication layer built on to of the OAuth2.0 authorization layer. It provides a simple way to verify the end user based on an authentication performed on a background server/service. Additionally it is able to pass a basic profile about the user to a RESTful HTTP API for authorization using JSON.

"OpenId Connect allows a range of clients, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, supporting optional features such as encryption of identity data, discovery of OpenID Providers, and session management." -Wikipedia

For .NET there is a Nuget package for the Identity Server component called IdentityServer3. There is a pretty in-depth getting started tutorial on how to get a simple MVC/Web-API running with IdentityServer3.

Web Apps vs Web APIs and Cookies vs Tokens

  • Typically Web Apps are the traditional server-side applications that use cookie-based authentication.

  • Web APIs, on the other hand, represent for us a new breed of applications, typically single page apps (like Angular, Ember, Backbone, etc.) or native mobile apps (like iOS, Android, etc.) which consume APIs (written in Node, Ruby, ASP.NET or even a mix of those) and will benefit from token based authentication.

You might want to read these articles for more context: Cookies vs Tokens. Getting auth right with Angular.JS and 10 Things You Should Know about Tokens.

  • Cookie-based authentication is implemented by each web platform differently, but at the end of the day, they all end up setting some cookie (tied to a session on the server) which represents the "authenticated user". On each request, that cookie is sent and the session is deserialized from some store (in memory if it's a single server or some persistent storage if it's a server farm).

  • Token-based authentication is implemented by generating a token when the user authenticates and then setting that token in the Authorization header of each subsequent request to your API. You want that token to be something standard, like JSON Web Tokens since you will find libraries in most of the platforms and you don't want to do your own crypto.

  • For both approaches you can get the same amount of information from the user. That's controlled by the scope parameter sent in the login request.

  • You can mix token-based authentication with cookie-based authentication. Take into account that cookies will work just fine if the web app and the API are served from the same domain, so you might not need token based authentication. If you want to call your APIs from JavaScript (instead of using the existing cookie) then somehow you have to set the id_token in your webpage. One way of doing it is by setting it on your layout/master page something like window.token = <%= id_token %>; and then you get it from anywhere in your JavaScript code.

P.S This is a great video on the subject called "Unifying Authentication & Delegated API Access for Mobile, Web and the Desktop with OpenID Connect and OAuth2 by Dominick Baier". Which should help shed a lot of light on the limitations of OAuth2.0 and how OpenId Connect attempts the solve them.

1
votes

I've managed to get around this issue by using cookies and refresh token.

This is how it's done,

  1. Encrypt and save access token & refresh token in a cookie.
  2. Validate token expiration date before making calls to web api.
  3. If token is expired, get new token using refresh token / clear user auth cookie and log him out.

I haven't tried it in the VS2013 SPA template. But as I see it, you could use step two to validate the token and if it is expired, call mvc controller end point to get a new access token.