Authentication: Don’t be Clever

HTTP API authentication has evolved through many forms over the years. As so-called RESTful APIs gained popularity, a variety of methods sprung up: key passing, plain-old HTTP Basic Auth, OAuth 1.0, OAuth 1.0a, OAuth 2.0 (and it’s 40 revision) and some less-common custom schemes. With the OAuth 2.0 specification finalized, things are finally starting to settle down and coalesce around a single auth mechanism. For publicly-available APIs, OAuth 2.0 should be on your list of requirements.

The most important thing to remember when implementing OAuth 2.0 is this: don’t be clever. The spec is big and scary, but thorough. There has been a lot of thought put into all of those requirements and you should take care to read through them carefully. Any deviation from the spec will result in consumers of your API having to learn a new concept or alter a pre-built library to work with your API. Custom implementations are non-obvious (even if well-documented) and will make for a frustrating developer experience.

Not every API requires a full OAuth 2.0 implementation. For the rest, I recommend keeping it as simple as possible. The two most common simple auth schemes are key passing and HTTP basic auth. In either case, if the traffic is going over the public internet you should require HTTPS to prevent any man-in-the-middle sniffing of API credentials. Support for basic auth is built in to every HTTP library and is well-understood so use that whenever possible.

If you implement OAuth 2.0, don’t forget about the first-run developer experience. Requiring the developer to set up the web infrastructure required to go through the auth flow and obtain a token for testing is an unnecessary burden. While there are tools to help with this, make it easy for the developer to generate a token from your web interface (App.net does a great job of this). Alternately you can support basic auth just for the developer that created the application so they can get up and running faster.

Validating Credentials

It is useful to provide a method to determine if credentials are valid. The root URL is a good place for this:

# basic auth example request
api.example.com
GET /
Authorization: Basic (base64 encoded credentials here)

# oauth 2.0 example request
api.example.com
GET /?access_token=foo

If successful the API should return some basic info about the user. We could add more information to the root response if we want to support hypermedia operations, but that’s for another post.

HTTP/1.1 200 OK
Content-Type: text/json
{
    "organization" : {
         "name" : "Runscope",
         "id" : "O94107"
    },
    "user" : {
        "name" : "John Sheehan"
        "id" : 123
        "subscription_expires" : 1356976799
    }
}

Error Messages

If the credentials are invalid, the API will return an error. Here’s an example of an error response that could be returned on any resource that is accessed with invalid credentials.

HTTP/1.1 401 Unauthorized
Content-Type: text/json
{
    "error" : {
        "code" : 401
        "message" : "The credentials provided are invalid."
        "more_info_url" : "http://example.com/help/errors/401"
    }
}

Side note: You may be asking, why not just put the error information at the top level of the JSON returned? If you’re using a dynamic language to parse your JSON it’s a little less typing to have the error data at the top level. If you are using something strongly-typed you’ll want to have responses from a single endpoint that don’t conflict with each other. This is a lesson I’ve learned from RestSharp. If you can’t map all of the responses from a single endpoint to a single class definition (even if you’re only partially populating it), you’re in for a world of hurt trying to wrangle around it.

With a simple request to / we can now validate credentials and get other important information about the user to display in our app.

Conclusion

By using standardized, common authentication schemes you can reduce the cognitive overhead for the developer consuming your API and avoid getting into unfamiliar, untested security situations. Authentication is the first thing any developer using your API will have to deal with and it’s those first few moments that are crucial to their success using your API to solve their problem. Take care to make the first impression a good one.

13 thoughts on “Authentication: Don’t be Clever

  1. Christopher Deutsch

    If you want to use a single, long “ApiKey” for authentication, do you recommend using Basic Auth? And if so, is it acceptable to just pass that “ApiKey” as the username and leave the password blank?

    Reply
    1. John Sheehan Post author

      Stripe does that and asks you to put it in the username field with a blank password. I would allow it to be sent in either and if either match, accept it.

      Reply
  2. Jason Harmon

    Any opinion on the value of ‘refresh tokens’ in the OAuth2 spec? Seems to add some time expiring insurance on any potential replay attacks is a good thing in terms of security. I would think in certain higher security situations that might be appropriate.

    Reply
    1. John Sheehan Post author

      At IFTTT they were the bane of my existence. I think they’re fine and probably a good idea in most cases, just allow for a way to create non-expiring tokens for pre-approved apps that you trust.

      Reply
  3. Pingback: Authentication: Don’t be Clever | CodingScoop | Scoop.it

  4. Weng Fu

    I think password is sufficient for most web sites. The password should not be save in database because of SQL injector. It is better to save password in hided password file separate from database. Password file should be encrypted by ROT13 to prevent hacker access to the file.

    Reply
    1. Fadi E. (itoctopus)

      @Weng,

      SQL injection affects the whole database – and not just a table with your uid/pwds. SQL injection is the outcome of some seriously badly written code somewhere in your application – so you need to make sure that your code is secure and resilient to any SQL injection attacks.

      Using files to store passwords has the exact same problem (your application might have some issues with filesystem security) and can lead to performance issues especially if you have a long list of logins. Additionally, when migrating a website from one place to another, you will need to remember to copy that file or else the website won’t work.

      Reply
  5. Pingback: Authentication: Don't be Clever | nodeJS and REST APIs | Scoop.it

  6. Pingback: Authentication: Don't be Clever | kernicPanel | Scoop.it

  7. Pingback: Tictail API launch focuses on UX

Leave a Reply