Most of us already work with multiple third-party API services to fulfill your business logic. And yes, most of them have a different way of authentication.
With this post, I'm going to tear down some popular ways. Let's jump in.
Simple API Key
80% of services out there are used this way. We simply ask for the API Key (from the admin dashboard) then boom, we can use the services in no time.
We need to provide the API KEY either in the header, URL params or POST body.
$http->get('https://awesome-service.com/v1/api/awesomeness', [ 'headers' => [ 'X_API_KEY' => 'generated_api_key_here', ], ]);
The easiest approach, the implementation is straightforward.
API consumers are happy because it is super easy to use too.
The API KEY will be available forever.
The API KEY will be available forever (lol). Check the next one.
Having the worst auth security. If you expose your key, everybody can use your key to access the service (a real danger if the service would contain sensitive data, money,...). As well as the rate limiting.
To avoid this, create your own wrapper layer in your backend service to handle the 3rd-party requests. Never return/use the key in FE/Mobile.
- 💯 if the service provides an SDK package (for PHP, Node,...) - save time hehe.
Alternatively, the service provider should have whitelisting/backlisting IP addresses too.
Once the security concerns are solved, then it is always my go-to solution.
Simple Login to Retain the API Key
Same as about, but there would be 2 more improvements:
We can ask for the API Key. But this API Key is only used for
login(to retain another API Key which can be used to request other endpoints - I'll call it as
access_tokenwill have a lifespan (of 30m, 1h,...)
So our code would be like this:
$dynamicToken = $http->post('https://strict-service.com/v1/api/auth', [ 'login_id' => 'seth.tran', 'api_key' => 'generated_api_key_here', ]); $http->get('https://strict-service.com/v1/api/classes', [ 'headers' => [ 'X_API_KEY' => $dynamicToken, ], ]);
Stricter security than the 1st point.
API Key now has a lifespan.
In our backend services, we need to add the lifespan check (in order to refresh before requesting if the token is expired)
- If SDK available & cover this, then 💯
This way is really strict as well. It also comes along with the 1st point which we still need a generated API KEY (to use as an identifier).
This will involve 1 or multiple keys, such as SSH, GPG,... even an SSL certificate (lol).
At first, both parties will share the public key with each other. Then importing it.
When making any API request to the Service, our BE service needs to:
Use our private key & their public key to encrypt the payload.
Make the call along with the generated API Key.
Once we received the response payload, we might need to:
- Decrypt the payload (same keys approach above)
$encryptedPayload = GpgService::forBankService() ->encrypt([ 'to' => [ 'name' => 'Seth Phat', 'account' => '01010101010101', 'swift_code' => 'ABCXYZ', ], 'amount' => 100.00, 'currency' => 'USD', ]); $http->post('https://bank-service.com/v1/api/transfer', [ 'headers' => [ 'X_API_KEY' => 'my_generated_token', ], 'body' => $encryptedPayload, ]);
Pretty strict, right?
- Most secure way. The data will always be encrypted on both ends.
Take a lot of time to set up (in our infra, on the service's side,...). It is normal to take about a week or two.
Our BE service needs to implement the encrypt & decrypt logic.
- Trust me, once you are dealing with this, the performance is not really good.
Most services following this way won't provide any SDK packages. Only documentation.
Depending on the services, we have to re-generate the keys/SSL certificates (some annually, some every 5 years,...)
For normal services that won't contain sensitive data, I'd always go for the 1st point (with multiple security options of course)
For sensitive services: security over performance - key exchange.
What would you choose?