Be consistent
Some may say this is unnecessary (and not too long ago I would have agreed) but these days, with so many auth protocols, if we use the Authorization
header to pass an API key, worth informing the type too because API keys are not self-descriptive per se 1.
Why do I think it worth it? Because nowadays supporting different authentication or/and authorization protocols have become a must-have. If we plan to use the Authorization
header for all these protocols, we have to make our auth service consistent. The way to communicate what kind of token we send and what authorization protocol should be applied should go in the header too.
Authorization: Basic XXXX
Authorization: Digest XXXX
Authorization: Bearer XXXX
Authorization: ApiKey-v1 XXXX
Authorization: ApiKey-v2 XXXX
I used to don't care about this, but after working with mobile clients or sensors, which updates were not guaranteed, I started to. I started to be more consistent in the way I implement security so that I can keep backwards compatibility. With the token's type informed I can invalidate requests from a specific set of clients (the outdated ones), add new schemes and differentiate old clients from new ones, change auth validations for one or another scheme without causing breaking changes. I also can apply specific rules in the API Gateways based on the authorization scheme informed. For example, I can redirect old schemes to specific versions of my web APIs which are deployed apart from the main ones.
Concerns
The problems I faced implementing my own schemes has been similar to the one commented.
On the other hand, I found a consideration that a custom Authorization
scheme can be unexpected and unsupported by some clients and leads to custom code anyway
Say clients, say libraries, frameworks, reverse proxies. A custom header can be rejected or ignored. In the worse of the cases, it can also collide.
Advantages
One important advantage is cache. Shared caches won't cache the header (and that's good of course) unless you say otherwise.
So Authorization or custom header?
To my experience, both take me almost the same work and time to implement, with a slight difference with having more room for design when I have implemented custom headers. However, more room for design also meant more chances to overcomplicate things.
Technically there could be very little or no difference among the two, but I have found the consistency to be a property of any solution I value for what it provides me, clearness and understanding. In my case, adding new schemes was reduced to add 2 new abstractions (implemented by the same concrete class): TokenHandler and TokenValidator. The handler only checks whether the request header Authorization informs the supported scheme. The Validator is anything I need to validate the token. Altogether working from a single request filter, instead of from a chain of filters or a big ball of mud.
1: I find this answer to be very clear regarding API Keys
We need to think in the best design API starting from the API objective.
Your API objective is:
service that forwards/unifies our API calls to external platforms/services
So, you start with posts
endpoint:
POST /posts/
Body
{
title: "Hello",
text: "Hello World"
}
But this post
can posted to Facebook, LinkedIn, etc and you need the user reference also.
You can pass this informations on the body, of course. But:
- Mix exclusive platform attributes on same body. The Twitter, per example, does not have a
title
or some platforms has support for tags
. This attributes will be ignored for platforms that not support them. The server will need to figure out at runtime what kind of platform he is receiving and looking if all required fields are there for that platform. The code could easily be a mess.
- Client will handle the API complexity. As some attributes are exclusive for some platforms, the client needs to control what group of attributes he needs to send for each platform for the same endpoint.
- Document this API will be a difficult thing to do. Imagine put a Swagger on that. Probably the client will have a hard time trying to figure out what informations is supported for each platform.
- Support for new platforms will be harder. Maybe you would like to add a new platform that needs a new information to be passed together with the user reference (see my Slack example).
- Versioning is not possible for each platform. You will not able to version each platform individually, what could be a nice feature to follow the changes of each platform.
And etc. You can think in a lot of possibilities and particularities of each platform.
As you can see, use a single endpoint is a risk.
So seems good to me separate in different endpoints:
/platforms/facebook/
/platforms/twitter/
/platforms/google-plus/
And the URL endpoint and body
of each one can have his particularities. Divide and conquer.
You can understand this as /platforms/{platform}
, but be careful. Threat this as generic can bring some problems if you need more flexibility on the URL endpoint. Per example, to create a post on Slack you need the platform, user and a channel:
/platforms/slack/users/slav/channels/rest-questions/
Thinking in this way, you can separate the platforms and can change each one without modify the others that are working. The GET
and another HTTP methods are natural to use in this case too.
About the verbosity, is not a problem if this brings clarity about the use of your API.
Best Answer
In HTTP, there is a
Authorization
header for that.While it is usually used to provide users' credentials, in a case of an API, it can contain the ID of the client and the corresponding API key.
There are several benefits:
Support from different frameworks. Many frameworks will expect
Authorization
header in order to do authentication. Not using it will force to write additional code to feed those frameworks with custom values.Support from different tools. For instance CURL.
Less “WTF where do I find/put this API key?!” from new developers joining the team (or developers designing new clients for your API).
You can then use HTTP status code definitions such as
401 Unauthorized
, for which:Moving it to the body of the request can become quickly painful. Most frameworks and tools don't make it very straightforward to add a body to a request, which may make your API more difficult than it needs to be.