user1082754
user1082754

Reputation:

Authentication for isomorphic web app with separate website and API servers

I have developed a stateless API on a server at api.com. Some API endpoints require authentication.

I have a website on a separate server at website.com. When a user authenticates with the website, the website server needs retrieve some data from an API endpoint which requires authentication (/tweets, for example). This data will be used in the server response (to render the tweets, for example).

The server response will also download some JavaScript in the browser that will subsequently need to retrieve (via XMLHttpRequests (XHR)) some data from an API endpoint which requires authentication (/tweets, for example).

This architecture represents an isomorphic web application. The server renders the whole page when requested, and thereafter the client handles user actions using JavaScript.

--

At a very basic level, I could use HTTP Basic Authentication for both website.com and api.com. However, the browser would prompt the user to enter their credentials when they first login to website.com, and repeatedly when the client makes an XHR to a endpoint requiring authentication.

I want the user to login with their credentials once at website.com. This is similar to the current Twitter website. Once you login to twitter.com, the website server identifies you as authenticated and responds with a HTML page containing JavaScript downloads. The JavaScript app then (presumably) makes authenticated XHRs to the stateless Twitter API.

The API is a separate server by design. Eventually the API could be opened up for third parties, although this is not an initial requirement.

How can I achieve this? I'm looking for:

Both would be great!

Upvotes: 13

Views: 2338

Answers (5)

Julian
Julian

Reputation: 4366

The situation you describe is exactly what OAuth is designed for: a client authorizes with one server and then obtains access to resources on another server. In your case, website.com is the authorization server and api.com is the resource server. In a nutshell, the authorization server sends an access token to the client, which the client can then pass on to the resource server to prove that they have permission to access the resource. In order for this to work, the resource server (api.com) needs to either check back with the authorization server (website.com) to verify that the token is valid or be informed about the token in advance. So there is a triangle of communication between the client, the authorization server and the resource server in which a shared secret is passed around. Because of this, it is absolutely necessary to use secure connections (HTTPS) in all parts of the chain; otherwise, someone could intercept the token and pretend to be the authorized client. This is kept within reasonable bounds by using limited-access tokens which do not fully authenticate the user, but it is nonetheless a problem that you should try to prevent.

While theoretically secure, OAuth is a complicated system and it is hard to get right. Some people think it is practically impossible to get right (notably Eran Hammer, the original lead author of the OAuth 2.0 specification who decided to withdraw from the working group). However, given that you need to use HTTPS anyway, you could avoid OAuth altogether and instead use a little-known builtin feature of HTTPS (or actually, TLS) called (surprise!) client authentication.

As you probably already know, in the HTTPS protocol, the server (website.com) uses a certificate signed by a trusted authority to authenticate itself. This is a well understood and very secure mechanism (at least by internet standards), provided that the certificate is uncompromised and that the latest version of TLS is used. The client can do the same, i.e. authenticate with a certificate that was signed by a trusted authority. The latter authority can be the server (website.com) for this purpose, because the server can trust itself. So the elegance of TLS client authentication is that the client does not need to contact a third party in order to obtain a certificate; the client and the server can cooperate to provide the client with a certificate that the server can trust. This is potentially even very user-friendly, because the client certificate needs to be transferred and installed only once and can then be used for authentication on subsequent sessions, possibly without the user even needing to enter a password. The same client certificate can also be used for HTTPS connections with other servers (e.g. api.com), provided that those servers know about the certificate and trust the authority that signed it (website.com).

TLS client authentication is likely to be more secure than OAuth, while it might require less interaction from the user overall (depending on the way in which the client certificate is handled in the browser). On the other hand, most users are probably unfamiliar with the particular mechanics of TLS client authentication. If users need to log in from many different devices or need to authenticate to many different servers, this workflow may be confusing or cumbersome because each device needs to have a certificate and the certificate may have to be selected manually by the user when a new server is visited for the first time.

To summarize:

  • In both cases, website.com provides the client with a means to authorize for access to api.com, which api.com needs to know about. So api.com cannot be 100% stateless; it needs to have some knowledge about the means of authorization that website.com communicated with the client.
  • Both cases require a secure connection (HTTPS).
  • In OAuth, the means to authorization is a "shared secret" limited access token (also known as "pseudoauthentication"), while in TLS client authentication, it is a private certificate that fully authenticates the client because it was signed by a trusted authority.
  • In OAuth, authorization is done on the data layer (applications explicitly communicate the access token) while in TLS client authentication, authentication is done on the transport layer (meaning that your API does not actually need to be aware of authentication or even authorization, if the webserver is configured to allow certain endpoints only to authenticated clients).
  • TLS client authentication is probably more trustworthy, but OAuth is probably more familiar to users because it works with password logins "as usual".

Some clarifications in response to the comments:

How does website.com know the user is logged in? How does website.com remember the user is logged in (i.e. between browser refreshes)?

By storing the access token in a secure cookie on the client side. On every request from the client to website.com, the access token is included in the request headers. This way, website.com can be assured that every request is either authenticated (if the request contains the access token, i.e. the user is logged in), or the visitor is a guest.

How does the browser make authenticated XHR requests?

By sending the access token in the request header, just like for website.com. Obviously, this requires the cookie to be accessible to the client.

website.com needs to authenticate with api.com when creating the server response

When it does that, it just sends a (HTTPS) request on the user's behalf. It's the same thing where the access token is included in the request headers. website.com always has the access token of the user when it does this, because it either is about to provide it to the user or it just received it from the user.


Further information on Wikipedia:

Upvotes: 10

Ashley Lee
Ashley Lee

Reputation: 3986

As Julian mentioned OAuth is complicated and hard to get right. I would find a trusted opensource project and use that as your Identity Server.

Also, instead of OAuth, I would look into OpenID Connect. It is a relatively new protocol (Jan 2014), but has been getting a lot of attention. Google+ for example is using it. It combines the authorization framework of OAuth and adds the identity and authentication framework on top. OAuth was never truly designed for that, which is one of the reasons why Eran left the project. This new protocol is the future of Single Sign On and will replace WS-Federation and SAML. http://openid.net/connect/

Here are all the current libraries available: http://openid.net/developers/libraries/

Again, if you're using C#/.NET, here is their project currently in Beta 3 (should be live in Januaray) that provides every possible configurable scenario with examples. If nothing else, it gives you the code to see how you can implement it. https://github.com/thinktecture/Thinktecture.IdentityServer.v3.Samples

See this talk for more details: http://vimeo.com/97344501

Hopefully this gives you some food for thought.

Upvotes: 1

dagnelies
dagnelies

Reputation: 5319

Here is a simple way. It adds some overhead/latency, but at least it works and is dead simple:

let website.com act as a proxy and forward all calls to api.com

browser <-> https://website.com/api/url <-> https://api.com/url

You can just reuse the creditentials to make a separate session from website.com to api.com

Upvotes: 0

alekseevi15
alekseevi15

Reputation: 1782

Basically, you need to decide whether you want to authorize website.com domain to use api.com methods, or to authorize users of website.com to use api.com. From your description I understand that your are talking about the second case.

Then some kind of OAuth(OAuth2.0) implementation could be suitable for you with(probably) a single authentication point for the all your sites: it could be the passport.com. When user wants to use api.com or website.com or any other your site, he will be redirected to passport.com and required to authenticate there. After authentication on passport.com, user will be redirected back and provided with authorization code, that is used to request token, that, in turn, could be used by api.com and website.com to get information about the user(roles, permissions etc). On website.com you could use this token to access api.com, because api.com could use this token to validate user against passport.com. Also you could use this token to request info from api.com by AJAX(you need to overcome the Same Origin Policy problem, though, but it's feasible).

UPDATE:

The idea is that user needs to be authenticated on passport.com to use website.com & api.com. From the OAuth2.0 standpoint you could say that user authorizes website.com & api.com to use his info on passport.com. So authentication cookie exists only for passport.com domain and is not sent anywhere else(to website.com or api.com). This is according to the Same Origin Policy.

So more detailed description of OAuth2.0 implementation in your case would be:

  1. User wants(or have to) to be authenticated on website.com. Request to the passport.com is performed(with specified REDIRECT_URL on website.com where to return later): passport.com/auth/?redirectTo=REDIRECT_URL

    If user is not authenticated on passport.com(there is no Auth cookie there) then login page of passport.com is displayed to him. When authenticated, new AUTHORIZATION_CODE is saved on passport.com(in database) for that user.

  2. After authentication on passport.com(or if user already was authenticated there) passport.com redirects user back to REDIRECT_URL with AUTHORIZATION_CODE in querystring: {REDIRECT_URL}?code=AUTHORIZATION_CODE

  3. Website.com uses this AUTHORIZATION_CODE to request ACCESS_TOKEN from passport.com: passport.com/token/?code=AUTHORIZATION_CODE

  4. Having ACCESS_TOKEN, website.com could use it to request information about user from passport.com. Plus you could pass this ACCESS_TOKEN to api.com when requesting something from api.com. api.com could check user's identity against passport.com to check if he has enough permissions. Is it safe to pass ACCESS_TOKEN to api.com? You need to provide some sort of key to api.com anyway(if api.com is not a public api), so using this approach at least ACCESS_TOKEN is not a static one: it has lifetime, and it's user-based.

Again, it's very simplified OAuth2.0 example without secret keys, scopes, access grants and few other details.

Upvotes: 0

Timothy Ha
Timothy Ha

Reputation: 399

I think something like this can be done

1) User logs in at website.com, website.com will create a temporary token T for future API usage

2) Whenever some data is required from api.com, website will request that data and send token T in the request api.com/getdata/params=...&token=T

This request is better done with SSL to protect the token.

Please also check http://en.wikipedia.org/wiki/Cross-origin_resource_sharing - not all browsers will let you request data from another domain from Javascript.

3) When api.com receives such a request, it will make a separate and secret connection to website.com, something like website.com/checktoken/?token=T and get all necessary information about the user at website.com to send him relevant data

4) User gets all information, not leaving website.com and not having to authenticate at two places

Upvotes: 0

Related Questions