Ibuildings blog

afbeelding van relaxnow

Secure your REST API with OAuth2 Implicit Grant

First let's look at this requirement of having an 'open' API, next we'll look at some ways you can secure your API and finally we'll dive into OAuth2 Implicit Grants.

Open APIs

The answer to that is that while you think you want an Open API, you should think long and hard before leaving an API call open for everyone to use. In fact all you are probably doing is opening yourself up for a Denial Of Service attack by abusive clients. Most of the time for an API you want to identify clients for rate limiting (you ARE implementing rate limiting in your API right?). Most popular APIs like Google Custom Search or Twitter are already doing this. As an aside if you're not implementing rate limiting per client, go read Steve's Platform Rant on how DOS attacks don't just come from external services. Rate limiting can be as simple as an in memory store that increments a counter.  

API Keys or HTTP Basic / Digest

One way APIs typically implement security is either with API keys or some other sort of credentials (like username / password). The problem with normal API keys or regular credentials over HTTP Basic or Digest is that they are usually assigned on a per application basis. Unfortunately as a Single Page Application is all client based, you would HAVE to send your API key to the browser, where anyone can view-source, copy & paste, in effect opening those application-wide credentials for the world to use. Now, you're never going to get around this problem. You have to send some credentials to the client and they will always be viewable. In fact, even if JavaScript was a compiled language it's not unthinkable (though far less likely) that someone could decompile your application. Code has to be readable to be runnable and with that it it's impossible to keep any secrets on the client (or at least keep it completely). No, instead of application wide keys we have to identify each individual user separately. While this does not mean that keys will not get lost, it only means that the keys for that user are lost. Preferably with as much information as we can so we can safely throttle / block abusive users.  

Plain old Session Cookies

If you have an API that's meant for internal use and all concerned applications are running on the same domain then there is nothing wrong with just using a domain cookie or the PHP session. A typical scenario would be where you have a service / API with a single JavaScript client. In this case it's usually easiest just to let your users log in on the API and let it set a cookie for it's own domain. Watch out for RESTafarians though! Using cookies if you're talking to a REST service is bound to get your some flak from REST purists. While this is a perfectly fine solution for this scenario, it isn't very suitable for when you want third party applications to make use of your API. Your users should have no problem entering their ExampleCorp username and password for the ExampleCorp CRM and the ExampleCorp CMS on http://cms.examplecorp.com and http://crm.examplecorp.com. But you don't really want them to enter their user credentials for your site on twitter.com, or facebook.com, or random-site-with-not-so-secure-database.com, just so these sites can access your users data. Thereby teaching your users to give out their user credentials for your service to other services. It is dangerous to train your users to use their credentials on other urls or with other companies. You can't control the safety of your users credentials outside of your own domain and you can't guarantee that when your user is done using twitter.com, Twitter will not hold on to your users credentials.  

Enter OAuth2 Implicit Grant

Fortunately letting a user log in to another application is something that is a solved problem with OAuth. Unfortunately OAuth 1 wasn't designed with SPAs in mind and relies on being able to identify the client only, which has the same problems as using a (secret) API key. Which is, partly, why OAuth 2 (RFC6749) introduced multiple 'Grant types':

  • Authorization Code, 'normal' flow for 'trusted clients', like server to server communication
  • Implicit, special flow for 'clients that can't keep secrets'.
  • Resource Owner Password Credentials, backwards compatiblity, client can ask user for username / password
  • Client Credentials, basically API keys, useful when user authentication is not necessary

Every flow is useful in it's own context, but seeing as we're interested in authentication with SPAs, let's look deeper into OAuth2 Implicit Grant. Now in order to illustrate how the OAuth 2 Implicit Grant flow works, let's use Twitter as an example.

The story of a login

Say I'm creating a new service that uses ExtJS to display all tweets related to OAuth from my Twitter timeline, let's call it OAuthTwitter or OAT. In this story there are 5 players: - Me, the user or Resource Owner (RO) - Chrome, my browser or User Agent (UA) - OAT, the ExtJS application that wants access, or the Client - Twitter, where my tweets are stored, the Resource Server (RS) and coincidentally also where I need to log in (note they need not be the same!). This means that Twitter is also the Authorization Server (AS). Now when I start I, the user, tell Chrome to go https://oat.example.com. Chrome (UA) loads the page and executes the (ExtJS) JavaScript. ExtJS (Client) sees that it doesn't have an Access Token to get the data from Twitter and redirects Chrome to Twitter with a Client ID and a redirect URL (note that I would have had to register OATs Client ID and allowed redirect URLs earlier). I see a 'Grant OAT access to your data' screen, click okay and Twitter (AS) redirects me back with: https://oat.example.com/index.html#access_token=x (Note that the access token is in the URL Fragment, the server never sees this, only the Client). ExtJS (Client) now sees an access_token and sets the HTTP header 'Authorization: Bearer x' and asks the API (RS) for the data. The Twitter API (RS) checks for this token on in it's authentication store (AS), sees that it's valid for me and returns the data I want. As a UML Sequence Diagram: OAuth2 Implicit Grant example Sequence Diagram

This story is a 'Happy Path' scenario, it doesn't account for token expiry (Access Tokens should expire after a short duration) or errors signing in. In order to get the full details I'd recommend reading the RFC.  

Server implementation in PHP

The OAuth2 page lists several PHP Server libraries, if you are using Symfony2 you could take a look at the MIT Licensed FriendsOfSymfony OAuthServerBundle, which I have used before and works well.  

Client Implementation  with JavaScript

OAuth1 had a record of needing complicated clients because of the custom signing, fortunately OAuth2 relies on TLS to provide a secure channel so clients can be relatively simple. While StackOverflow would recommend you handcode your own client by looking at this gist, Andreas Åkre Solberg (better known perhaps for his work on SimpleSAMLphp), has also written an excellent client called JSO.  

OAuth2 Controversy

OAuth2 is certainly not without controversy and if you're going to rely on it significantly it's helpful to understand it's criticism. In July 2012, Eran Hammer resigned his role of lead author for the OAuth 2.0 project, withdrew from the IETF working group, and removed his name from the specification. Hammer pointed to a conflict between the web and enterprise cultures, citing the IETF as a community that is "all about enterprise use cases", that is "not capable of simple." What is now offered is a blueprint for an authorisation protocol, he says, and "that is the enterprise way", providing a "whole new frontier to sell consulting services and integration solutions." - OAuth2 Controversy on Wikipedia, clicking through on the links is certainly recommended. What follows are the main points against OAuth2 and how they relate to our use-case.  

Not a protocol, it's a framework to make protocols

Erans major point is that  OAuth2 is too loose, you can't make a generic client and/or server and expect it to cover all cases. However, as shown earlier, there is enough software that supports the most used parts of the specification and if you're using something unsupported it shouldn't be too hard to add. He raises a good point though: if you don't know what you're doing (at least on the server side) you're likely to introduce vulnerabilities in the implementation. Which is why I recommend that if you can get away with NOT doing OAuth2, I would. However for certain use-cases there simply isn't an option. If this is the case, you will need someone who has been trained in security. Though honestly, why would you let anyone NOT trained in security set up your authentication anyway?  

Relies on proper SSL / TLS implementation in clients

Maybe this is a problem for native applications (which we'll get to in a bit) where developers can ignore certificate errors? Browsers usually have pretty good TLS support, however it does make TLS the only line of real defence. This as opposed to signing, which offers some protection even without TLS. Which is important because TLS is hardly perfect. Certainly there is room for an additional layer of security here. However, even without this, it's still good enough unless you're dealing with highly sensitive data or functionality (I don't care if Twitter uses it, but I would get nervous if my bank or a heart-lung machine only relies on TLS for security).  

Don't use OAuth2 on Native clients

One specific problem that Eran points out is that OAuth2 is unsafe for Native mobile apps. In order to guarantee that third party applications do not receive the user credentials the third party would have to use a webview (easily touchjacked) or redirect to a browser, neither of which are great user experiences. And in order to return to your application you would have to use a custom scheme (like Skype does) but unfortunately these are not protected, so a malicious app could steal it. This is valid criticism an a good reason to a different OAuth2 grant type such as the OAuth2 Resource Owner Password Credentials grant type on Native applications and use username / password until Google and Apple (with help from IANA?) make sure schemes are uniquely registered to an application.  

Your access token may not actually come from the Authenticator

There is nothing stopping a user from copy-and-pasting an access token from another application / client into your application. So while the server is still correct in the user that's authenticated, there is a mismatch between who the server thinks it's talking to and who it's actually talking to. Example: I grant access to Facebook to use my Twitter, then I am redirected back to Facebook with an Access Token in the URL. I can then take this access token, and hand it to OAT. Now Twitter will think Facebook is requesting the data, when in fact OAT is. In practice this probably won't be an issue unless you're using client specific code (a code smell in its self) or relying on the 'scope' feature too much (hint: don't!). The scope feature allows you to give a specific client access to only a specific part of your API. For instance if you have a website that always has a scope 'all' and an admin environment where you can have a scope 'editor' or 'all'. The scope 'editor'  would allow the user to only place tweets, the scope 'all' might also allow the user to block tweets. Simply copying the Access Token from the website to the admin environment would grant the user extra rights! A simple solution would be to, in the client, check the Client ID of the access token received with the Authentication Server. However I would generally advise to not abuse 'scope' for authorization or write client specific code, in which case this is a non-issue.  

Long specification time

Unfortunately OAuth2 took 3 years to specify. In the mean time big players like Google and Facebook needed to have a solution, so they implemented draft specifications. And where these big players go, others follow. So we end up with a fair number of services and clients that support different versions of the OAuth2 draft specifications which may or may not be compatible with the final version. With time these clients and services will be updated or ignored. It's inconvenient for now, but will only get better in the future.  

Conclusion (TL;DR)

OAuth2 solves our use-case neatly, but it's not an easy solution and does require some security knowledge to properly implement. It's also still a relative young specification. That said, it already has some serious services (Facebook, GitHub, Google, SalesForce, etc) running it. If your use-case matches what OAuth2 has to offer, and you can't use another simpler protocol, you might want to have a look at OAuth2 (Implicit Grants). At least until Eran (or someone else) comes up with something better.   Thanks to Geert van der Ploeg for reviewing this for factual errors.

    Leave a reply