29

I have a question regarding how I should architecture a REST API using access token and API keys.

I have an API that needs authentication. I want to enable two use cases:

  1. The user logs into the interface using OAuth2 (password grant), and is granted a temporary access token. This token is used to authenticate the user. Therefore, the UI, that itself using the API, can fetch data and display it.

  2. I also want the user to have an API key to do the same calls, but in its application. Obviously, contrary to the access token, I want the API key to be long lived. Also, contrary to the access token that is tied to a given user (if we introduce a team mechanism, each user will have different access token, although they access the same resources), the API key should be unique to the project.

While similar, I'm not sure about how should I architecture that. I think that, internally, both API keys and access tokens should be stored in the same table, but API keys having no expiration time. Am I right?

One thing I'm not sure also is the concept of client. It seems that in the spec, the client is more like an external application. However may I actually use this concept here?

For instance, each "project" is actually a different client (although the client here is the same application, not an application created by a third-party developer).

Therefore, if user A creates an account on the system, a client A will be automatically created, with an access token tied to the client A, with a long-lived access token (aka API key). This can be used to perform API calls directly on his code, for instance.

Then, if user A logs into the dashboard, a temporary access token will be created, but this time with no application, but tied to the user, with a short life.

Does this sound sane? Have anyone already implemented such a thing?

Thanks!

Michael Gallego
  • 1,746
  • 1
  • 20
  • 29
  • 1
    API keys and access tokens are different concepts, model them separately and store them separately. – Pieter Herroelen Apr 17 '15 at 06:56
  • 1
    Yep, but how do you handle the fact that I want to access the API in a homogeneous way? I don't want to have a different ways of using the API. Whether that my web app consumes it using a temporary token or a client consuming the API directly from his webserver using API key. – Michael Gallego Apr 17 '15 at 19:57

3 Answers3

15

I think you should not consider the "API keys" a substitute of the access token.

You will have to use an access token anyway to bear the authentication between requests, so what you're actually modelling with your "API keys" is not a replacement of the usual bearer token, but rather a different client that provides other grant types to request a token with.

The flow I'd personally implement is the following:

  1. The user authenticates with the password grant type with a common client for every user (i.e. your "web app" client, which is public, i.e. it doesn't have a client_secret).
  2. The user can then create its own client. As per OAuth2 specs, these are not public, so they will consists of a client_id and a client_secret. These are what you call "API keys".
  3. A user will then be able to request an access token via their client, with any given grant type you want to support (e.g. direct client credentials, authorization code, implicit, third parties, etc.). You will have to stress quite a bit about the due safety practices on how to handle the client credentials.

Obviously, you will have to implement your OAuth2 server in such a way that clients can belong specific users, and have different acceptable grant types (i.e. you may not want to allow the password grant usage with a user client, while you may want to disallow any grant type other than the password one for your web app client).

You will then be able to define tokens TTLs, or lack thereof, on a per client or per grant type basis (e.g. access token requested via password grant, only usable by web app client, will have a short TTL, while authorization code grant will provide long lived tokens).
I would advise against complete lack of TTL, though, and rather use the refresh_token grant type to renew expired access tokens.

Furthermore, you'll probably have to define an authorization system of some some sort (ACL, RBAC, whatever), to define which client can do what. This means each access token should contain a reference to the client used for its creation.

So, to sum it up, here are the relations:

User has a Client.
Client has a User.
Client has many Token.
Token has a Client.
Token has a User.

YMMV on bidirectionals.

You should be able to implement everything I described with the most common OAuth2 servers implementations of any given platform.

TL;DR: "API keys" are actually OAuth2 clients.

Stefano Torresi
  • 686
  • 4
  • 10
  • Thanks Stefano ;). Sounds good and quite logical, although I don't really like the fact that user need to pass both a client_secret and client_id (which make things more complicated). I tried to understand hwo the Stripe API worked by analyzing the requests, and it seems they used Basic auth with a generated API key (that they should generate), and Bearer with access token (their webapp). – Michael Gallego Apr 28 '15 at 20:54
  • Well, you would use the client credentials only once to obtain an access token, while with Basic authentication you would send the actual credentials with every request instead. It's more complex, that's true, but it's also safer if made right (as it is anything with the whole OAuth2 vs Basic comparison). – Stefano Torresi Apr 30 '15 at 19:08
2

I wrote a post about the way to use access tokens for RESTful applications: https://templth.wordpress.com/2015/01/05/implementing-authentication-with-tokens-for-restful-applications/. Perhaps can this give some hints.

To answer your questions, I think that we need to have something homogeneous. I mean all your authentication mechanisms should be based on access tokens. Your API keys would allow you to get an access token that would be actually used for authentication.

As far as I understand, you have two kinds of users of your applications:

  • End-users using the Web UI (login with password through OAuth2)
  • Applications (login with API keys)

So I would implement these two kinds of users and make them the ability to get access tokens. Access tokens will be used in both cases to access the RESTful services.

In addition, I think that this answer can give you some other hints: Securing my REST API with OAuth while still allowing authentication via third party OAuth providers (using DotNetOpenAuth).

Hope it answers your question. Thierry

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
2

Thank you for your answer.

I'm actually quite experience with OAuth2 itself, my question was more targeted to API keys. I like the idea of an API key exchanging an access token but I think that does not work. The API key is fixed and does not change, while the access token can expires.

The question is: how the app can know if this is an access token or API keys. I mean, ok, let's say that in my database, each user has an "api_key" column in their database.

Contrary to an access token, the api_key does not expires (although the user can eventually rotate it). What I want, as I told, is homogeneous handling of authentication.

Case 1: my own web app do API calls

The workflow is as follow, using OAuth2:

  1. User enters his mail/password.
  2. Authorization server returns a temporary access token (eg.: "abc").
  3. In the web app, all API calls are done using this token. For instance: "/payments/1" with Authorization header: "Bearer abc".

Nice and simple.

Case 2: the user has an API key, that does not expire and can be used privately in their own app

Obviously, the authorization mechanism must stay the same. So:

  1. User goes into his account, and read that his API key is "def".
  2. In their server code, they can do the same call, with same authentication mechanism. So he can call "/payments/1" with Authorization: "Bearer def".

And it must work. As you can see, nothing has changed in both examples. They access the same resource, same authorization mechanism... but in one case we have an access token and in other case we have an API key. And I have no idea how I should implement that both from a database point of view and in the authorization code.

One potential idea I had is using different auth mechanism. For OAuth, it would be "Authorization: Bearer accessToken", while for API it would be a Basic authentication: "Authorization: Basic apiKey".

Does this sound good?

Michael Gallego
  • 1,746
  • 1
  • 20
  • 29
  • How can "Authorization server returns a temporary access token (eg.: "abc")." by validating user credentials ? For that Authorization server has to be part of Resource Server right ? – Sundar Rajan Sep 24 '19 at 10:39