FastAPI - API Keys

Secure FastAPI with API Keys in Python.

FastAPI - API Keys
regularguy.ethvia unsplash

Secure your API using HTTP headers, and manage your keys in a database


Alright, so you built a shiny new API, and it works great - but you sent it to your grandma, who sent it to her bingo friends, and now a bunch of golden girls are burning a hole in your pocket because they keep refreshing the bingo checker API you built.

That's where API keys come in. They are the most basic form of authentication and authorization, and an easy way to control usage of your API (we don't cover usage tracking in this article)

This article is standalone, but if you are completely new to FastAPI, I suggest reading the previous article, which goes into more depth on how to send requests. Or check out the public docs.

Who Should Read This?

Anyone who wants a simple and effective way to secure their APIs using a simple API key, but doesn't want to be stuck with a "toy" approach that isn't extensible and won't actually work in production.

Fastpi-users is also useful package that can be used to create more complex authorization flows, not needed for a simple API key implementation though.

The FastAPI HelloWorld

As always we start with our minimal application - nothing special here. If you are totally new to FastAPI and don't understand this code snippet, read this quickstart (only takes a couple minutes)

Securing the API

We want our client to be able to pass a valid API key via an HTTP header in order to use our API. This is relatively simple, we create a new dependency for our API:

This will invoke the function api_key_auth on the request prior to executing our business logic. Let's take a look at what this function looks like:

We extract the api-key from the HTTP request by looking at the "x-api-key" header, then we see if it exists in our local API_KEYS. If it doesn't - we throw a 401 unauthorized. Doesn't get any simpler than that.

Using a Database

The toy implementation leaves some things to be desired. A better implementation would be to load the API keys from a database. For this example, we will use Firestore just because its very simple to setup, but any key-value store will work. Setting up our Google Cloud Account and the Firestore table is beyond the scope of this article, but the docs will bring you up to speed.

We create an ApiKeyStore class that will abstract our client instantiation and interactions, and create a singleton api_key_store .

Next, we modify our API key auth handler to query our ApiKeyStore instead of looking at a local list of keys:

Now, if we make a request with an invalid API key, we can see the failure in our logs:

If we use a valid key, however, the request succeeds:

The problem is we've added a call to our database in the critical path of our API - which means we have increased the latency of our API, and made us vulnerable to transient database issues. The quick fix for this is to use cachetools and cache our "does_api_key_exist" function call. Like so:

Now, on subsequent requests, we do not need to call Firestore (we will refresh cache every 60 seconds).

We could make this move efficient by just periodically reloading the entire API key list in the background. This would be suitable if you are concerned about your database request volume - but unless you have a lot of customers and are very sensitive to latency / database usage, the current implementation will work fine.

One More Thing- HTTPS!

The API keys will be encrypted in transit to/from Firestore (via TLS), are encrypted at rest in Firestore, but are still visible in plaintext when our clients make requests - we need to enable HTTPS!

We'll stick with a simple self-signed certificate approach for this article. For more details on FastAPI HTTPS, please see these docs.

Create the self signed keys:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./self-signed.key -out ./self-signed.crt

And when you launch your web server, specify the key and cert:

uvicorn main:app --ssl-keyfile .\self-signed.key --ssl-certfile .\self-signed.crt

And that is all there is to it! Requests can now be made via HTTPS, and the client's API key in the headers will be encrypted.

Conclusion

You can integrate your API key table into your user creation workflows, allowing customers to create and delete API keys as they please. Next step will be to track customer usage using the API key for quota and billing purposes.

To see the code used in this article, visit the Github repo.

References

Subscribe to VidaVolta

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe