In my work as a Data Rebel, I encounter many challenges to help unlock, enrich and use data to help our customers. In the last couple of years, I have come to the conclusion that at this moment in time, the best way to scale and provide data-driven solutions is by creating API’s. These API’s can be used and consumed by your own API’s our by other consumers like for example a customer-faced website.
As our team makes use of Python in almost all cases, finding the right library can significantly speed up the development process. At this moment we use the Hug library a lot which has served us greatly, but I think it is important to keep an open mind and see what projects are started, evolve and become more mainstream.
A couple of months ago I accidentally stumbled upon FastAPI. At first glance, I thought: “Hé this looks really interesting: solid documentation and a solid foundation (Starlette) ”, but I didn’t have the time to play around with it.
As time progressed, I felt that I had to try out FastAPI and I am really glad I did this! The ease of use, the documentation, the already great community and frequent updates, make it a joy to work with. Sebastián Ramírez, the founder of this project is, is a great guy who spends a lot of time answering questions and thinking about challenges the users of the library face. As he is patient, helpful and supportive, he has created an informal culture of how we as a community should approach one-another and help each other out.
So what is our challenge?
As the solutions we as Data Rebels create have to be secure, we almost always make use of external authentications systems like Okta and Auth0. These systems take out the burden of having to create our own secure identity solution. Besides this, during development, we want to be able to login to the API directly to validate, debug and test it. Furthermore, we don’t want to share the Swagger documentation with the outside world, so these endpoints should be behind an authentication layer.
With the help of Sebastián I came up with the following approach:
- Login on the API by making use of Basic Authentication
- Receive a token in the form of a cookie
- When logging in to an endpoint, validate headers or the cookie containing a token
- Return the content
How to support this?
I started out with this base structure from the great FastAPI tutorial, which can be found here: https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/
With this, the basic set-up is in place. The missing pieces are:
- Create a custom class which makes use of Basic Authentication
- Creating an endpoint to trigger Basic Authentication and return a cookie with an authentication header
- Create an extended class to check for an Authorization header or Cookie header
- Create a logout function to clear the cookie
- Create custom endpoints for the documentation which should be secure endpoints
To make the example as simple as possible, I have kept the tutorial code intact as possible. The complete gist is the last one in this article, so you can scroll down if you want to skip the steps. We first start with the needed imports:
There are some extra imports needed, like for example the Starlette responses and requests.
The following gist shows the neede Basic Authentication class:
This class follows the pattern of the OAuth2PasswordBearer class. As can be seen, the scheme that is being checked is the basic scheme. The last line shows the creation of an object with the newly added auto_error keyword argument.
The next gist contains the creation of the endpoint:
Basically, this endpoint triggers the Basic Authentication window in your browser and returns a cookie containing the Authorization Bearer token to be used in future requests. The settings for the cookie, like domain, max_age and expires are from the Starlette library which can be found here. This endpoint keeps triggering a Basic Authentication window until valid credentials are filled in.
For this set-up to work, check that you have a valid domain, otherwise the cookie will not be set (this is what I ran into). For this example, I chose localtest.me which redirects you to your localhost /127.0.0.1.
Now for the new and extended OAuth2PasswordBearer class:
As mentioned, this class is an extension of the OAuth2PasswordBearer class which now includes checking for an Authorization header and an Authorization cookie. This will use a header as preference and falls back to a cookie. I could imagine that for override/testing purposes using a header is more flexible than a cookie as this can be set in Postman for example. Furthermore, you have to create this object and use this instead of the OAuth2PasswordBearer class.
As it should be easy to clear the cookie and login with another username/password, we need a logout endpoint:
Now for the final steps to be able to this are:
With the first line, I disable the automatic creation of the documentation/swagger endpoint, this to be able to test that we cannot access documentation without being logged in. The “/openapi.json” is needed to create the swagger documentation, so I create it manually with security in place. The same goes for the “/docs” endpoint.
Now start-up FastAPI and test if it works!
Go to http://localtest.me:8000:
Go to http://localtest.me:8000/login_basic:
Login and see the documentation:
And now access the http://localtest.me:8000/users/me endpoint:
Now trigger the logout action, http://localtest.me:8000/logout:
Check if you can access http://localtest.me:8000/docs and http://localtest.me:8000/openapi:
Great, it all works!
Hopefully, this post will help people who want to implement Basic Authentication in FastAPI. Many thanks go out to Sebastián Ramírez and the helpful FastAPI community, in particular, William Hayes who convinced me to create an article on this topic.
BVI Data Rebels is a company by BVI. Together we unleash the power of data-informed people!