Python FastAPI: Implementing SSL/HTTPS and CORS
In this installment of our Python FastAPI learning series, we explore the implementation of SSL/HTTPS for localhost
and also the enabling of Cross-Origin Resource Sharing, or CORS.
🐍 Index of the Complete Series.
Python FastAPI: Implementing SSL/HTTPS and CORS |
The code requires Python 3.12.4. Please refer to the following discussion on how to upgrade to Python 3.12.4.
🚀 Please note, complete code for this post can be downloaded from GitHub with:
git clone -b v0.6.0 https://github.com/behai-nguyen/fastapi_learning.git
❶ I have previously implemented both of these using Rust. Please refer to the following two posts below:
- Rust: actix-web get SSL/HTTPS for localhost. The sections on OpenSSL Toolkit and Generate a Self-Signed Encrypted Private Key and a Certificate are relevant to this post. If you’re not yet familiar with self-signed certificates, please refer to these sections. They discuss concepts that aren’t specific to Rust.
-
Rust: actix-web CORS, Cookies and AJAX calls.
The general discussion on CORS and AJAX calls in this post should
complement the official
FastAPI
tutorial CORS (Cross-Origin Resource Sharing).
❷ The changes to the code are quite minimal. The updated structure of the
project is outlined below. Since there are no changes to any files in the
./src
and ./tests
directories, we have omitted
these two from the illustration below.
– Please note, those marked with ★ are updated, and those marked with ☆ are new.
/home/behai/fastapi_learning/
.
├── cert ☆
│ ├── cert.pem
│ └── key.pem
├── .env ☆
├── logger_config.yaml
├── main.py ★
├── pyproject.toml ★
├── pytest.ini
├── README.md ★
└── ...
❸ The implementation of SSL/HTTPS for localhost
.
FastAPI
makes the implementation of SSL/HTTPS for localhost
fairly easy.
My principle reference is this post
Adding HTTPS to FastAPI. It is
more than a year old: the ssl
paramater in this call
uvicorn.run("main:app", host="0.0.0.0", port=8000, ssl=ssl_context)
is outdated, and so we don’t actually need the cryptography package
either.
The implementation is straightfoward. First, we need to generate the self-signed certificate files. Then, we need to get application to load and use these files.
⓵ To generate the self-signed certificate, on both Windows 10 and Ubuntu 22.10 run the command:
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
Be prepared, you will need to fill in the following prompts:
Country Name (2 letter code) [AU]: AU
State or Province Name (full name) [Some-State]: Victoria
Locality Name (eg, city) []: Melbourne
Organization Name (eg, company) [Internet Widgits Pty Ltd]: Personal
Organizational Unit Name (eg, section) []: Development
Common Name (e.g. server FQDN or YOUR name) []: <Windows: Full computer name> <Linux: hostname --fqdn>
Email Address []: behai_nguyen@hotmail.com
For Common Name (e.g. server FQDN or YOUR name)
, on Windows 10,
it is the full computer name, in Linux it is the hostname. For more information,
please refer to the
Generate a Self-Signed Encrypted Private Key and a Certificate
section mentioned previously.
The self-signed encrypted private key and the certificate files,
key.pem
and cert.pem
, are stored in the
./cert
sub-directory.
⓶ The code changes occur only in the
main.py
module:
82
83
84
85
86
87
88
89
90
91
92
93
#
# Remove the code block below to use the command:
#
# (venv) <venv path> uvicorn main:app --host 0.0.0.0 --port 5000 --ssl-keyfile ./cert/key.pem --ssl-certfile ./cert/cert.pem
#
# The command to run with the below code block:
#
# (venv) <venv path> python main.py
#
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=5000, \
ssl_keyfile="./cert/key.pem", ssl_certfile="./cert/cert.pem")
FastAPI certainly makes it a lot easier.
The available routes should now be accessed via https
:
-
GET
,https://0.0.0.0:port/admin/me
: Returns the currently logged-in user’s information in either JSON or HTML format. This route is accessible only toauthenticated sessions
. -
GET
,https://0.0.0.0:port/auth/login
: Returns the application login page in HTML format. -
POST
,https://0.0.0.0:port/auth/token
: Authenticates users. The response can be in either JSON or HTML format. -
POST
,https://0.0.0.0:port/auth/logout
: Logs out the currently logged-in or authenticated user. Currently, this redirects to the application’s HTML login page. -
GET
,https://0.0.0.0:port/
: This is the same ashttps://0.0.0.0:port/auth/login
. -
GET
,https://0.0.0.0:port/auth/home
: Returns the application home page in HTML format after a user has successfully logged in. This route is accessible only toauthenticated sessions
. -
GET
,https://0.0.0.0:port/api/me
: This is a duplicate ofhttps://0.0.0.0:port/admin/me
, but this route returns the currently logged-in user’s information in JSON only. -
POST
,https://0.0.0.0:port/api/login
: This is a duplicate ofhttps://0.0.0.0:port/auth/token
, but the response is in JSON only.
❹ Enabling Cross-Origin Resource Sharing, or CORS.
We can just follow the official tutorial page
CORS (Cross-Origin Resource Sharing) to enable
CORS. Instead of hard-coding the CORS information, we can load
them from the .env
file.
We will need the python-dotenv package. Please re-run the editable install command:
(venv) <venv path> pip install -e .
The
code changes in the main.py
module should be self-explanatory.
❺ CORS, AJAX and cross domain in action.
In this final section, we will illustrate how CORS behaves. We use the HTML page
ajax_test.html
which calls the function
runAjaxCrossDomain(…)
to send AJAX requests to the application. You can just copy this
ajax_test.html
page to your localhost
and
run it locally.
💥 Please note, about HTTPS and
localhost:
I have observed that some browsers,
such as DuckDuckGo, will not allow access to self-signed certificate endpoints
like https://localhost/...
or https://0.0.0.0/...
The error displayed is NET::ERR_CERT_AUTHORITY_INVALID
.
FireFox and other Chromium-based browsers warn about security risk,
just accept and proceed. You still might have problems accessing
https://0.0.0.0/...
endpoints via AJAX. I have found that,
first access a GET
endpoint,
such as https://192.168.0.16:5000/auth/login
, accept
the risk and proceed, and you should get a valid response. Then,
using the same browser to run the ajax_test.html
page.
This will get around the AJAX accessing problem.
⓵ The ALLOW_ORIGINS
for CORS is set to http://localhost
.
We will illustrate the following requests: logging in, retrieving the logged-in
user information, and finally, logging out.
● The login request:
URL: https://192.168.0.16:5000/auth/token
Method: POST
Content Type: application/x-www-form-urlencoded; charset=UTF-8
Body: username=behai_nguyen@hotmail.com&password=password
Header: {"x-expected-format": "application/json"}
We should receive a response as illustrated in the screenshot below:
● The request to retrieve the information of the logged-in user:
URL: https://192.168.0.16:5000/admin/me
Method: GET
Content Type: application/x-www-form-urlencoded; charset=UTF-8
Body:
Header: {"Authorization":"Bearer behai_nguyen@hotmail.com", "x-expected-format":"application/json"}
We don’t actually need to include the “Content Type” and the “Body” fields.”
We should receive a response as illustrated in the screenshot below:
● The logout request:
URL: https://192.168.0.16:5000/auth/logout
Method: GET
Content Type: application/x-www-form-urlencoded; charset=UTF-8
Body:
Header: {"Authorization":"Bearer behai_nguyen@hotmail.com"}
We don’t actually need to include the “Content Type” and the “Body” fields.”
We should receive a response as illustrated in the screenshot below:
⓶ The ALLOW_ORIGINS
for CORS is set to https://google.com
.
This means that the application expects AJAX requests to come from
https://google.com
. Since we are sending AJAX requests from
http://localhost
, they should be rejected. And indeed, they are,
as illustrated in the screenshots below:
❻ Writing this post has made me appreciate
FastAPI
even more 😂… Implementing SSL/HTTPS for localhost
is straightforward;
I didn’t expect it to be this easy.
Thank you for reading. I hope you find the information in this post useful. Stay safe, as always.
✿✿✿
Feature image source:
- https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper
- https://in.pinterest.com/pin/337277459600111737/
- https://www.python.org/downloads/release/python-3124/
- https://fastapi.tiangolo.com/
- https://1000logos.net/download-image/