How to deploy Python Dash app on App Engine, which interacts with Flask app on Cloud Run using Firebase authentication
In a business context, we are often interested in creating dashboards, which enable us to show images, graphs, tables, etc. There are many frameworks to create dashboards (a.k.a. frontend applications). For Python users, Plotly/Dash would be one of the options. Regarding platforms, Google Cloud Platform (GCP) provides a fully managed serverless platform, App Engine, where we can readily deploy a frontend application.
In a previous article, we discussed how to deploy a Flask app on Cloud Run with authentication. With the previous example, we are going to demonstrate two things:
- how to deploy a Dash application on AppEngine;
- how to configure the App Engine instance to interact with a Flask application on Cloud Run with authentication.
In terms of the first task, we can find numerous “How To” articles (e.g. by Data Science Campus) since it is not complicated. On the other hand, the second task is not straight-forward. We are going to demonstrate two different authentication methods, using either GCP Service Account Credentials or Firebase SDK in order to establish an interactive frontend on App Engine to Cloud Run instance.
This article is based on an example in the previous article.
Step 1: Create a Dash application
We are interested in a simple Dash application, which enables us to talk to a Cloud Run instance. Note, our Cloud Run instance is protected with Cloud Endpoints. Therefore, we need to keep in mind that:
- The frontend (Dash) application sends HTTPS requests to the Cloud Endpoint;
- The frontend application creates
jwt
as the authentication token for Cloud Endpoint.
Regarding the second point, we are going to show an example using a GCP Service Account Credentials first. However, the method is less flexible than an example using Firebase SDK. We will discuss this later.
In this example, we created a very simple Dash application. The functionality is,
- When users click
submit_button
, create an HTTPS request to Cloud Endpoint instance (endpoint_url
), which is already deployed based on my previous article steps; jwt
is generated by App Engine using a functiongenerate_jwt
since a GCP Service Account Credentials file is saved in the same folder;- Once the request is successfully made, display the response from Cloud Run.
Here is a sample code for the Dash application.
Step 2: Deploy the Dash application on App Engine
We are able to provide our own runtime by supplying a custom Docker image in order to deploy an application on App Engine. In this example, we need six files:
- The main python script for Dash (
main_v2.py
); - Create
jwt
for authentication (create_jwt_from_sa.py, my_credentials.json
); - Configuration file (
config_appengine_v2.yaml
); - Docker related files (
Dockerfile, requirements.txt
)
dashapp-ae-v2
├── main_v2.py # Main python file
├── create_jwt_from_sa.py # File to create jwt
├── config_appengine_v2.yaml # Config file for App Engine
├── my_credentials.json # GCP service account credentials
├── requirements.txt # List of python packages
└── Dockerfile # For Docker build
For create_jwt_from_sa.py
, you can find the details in my previous post. The details ofDockerfile
and config_appengine_v2.yaml
are below:
# DockerfileFROM python:3.7.5
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . /app
WORKDIR /app
EXPOSE 8050
CMD python main_v2.py
The template for App Engine config file can be found in the link.
# config_appengine_v2.yaml# Run by Python 3.7
runtime: python37# Machine config https://cloud.google.com/appengine/docs/standard
instance_class: B2
manual_scaling:
instances: 1# Timeout is 60sec
entrypoint: gunicorn -b :$PORT main_v2:app.server --timeout 60 --workers 1# Instance name
service: dashapp-ae-v2
handlers:
- url: /.*
script: auto
secure: always
Once these six files are saved in the same directory, deploy the application on App Engine.
$ gcloud app deploy --project=$MY_PROJECT_ID config_appengine_v2.yaml
Go to the App Engine tab on the GCP console. The application is named dashapp-ae-v2
. If we have never used App Engine, we might need to create an instance called default
. Click the name of the instance and check whether the Dash application is successfully deployed or not.
Common error: If we see message Error: Server Error
when we open the instance, we need to check out the logs of the instance. Normally we forgot to write specific python packages on requirements.txt
.
It is time to check the functionality. If successful, the Dash application on App Engine sends an HTTPS request to Cloud Run, protected by Cloud Endpoint.
Results: The message from the instance is Hello from body, John Doe
, which is the expected result. Therefore, we are able to deploy a Dash application on App Engine, which communicates to our existing API on Cloud Run. Great job!
Step 3: Problem formulation
Someone might have noticed that this application is vulnerable and less flexible as:
- The Cloud Run instance is protected by Cloud Endpoint though, the GCP Service Account Credentials file is available on App Engine instance;
- Therefore, if anyone knows the App Engine instance location (i.e. URL), they can readily “break into” Cloud Endpoint and Cloud Run instance;
- Regarding general web applications, we would like to switch users on the same application; however, the App Engine instance has a single user only — the user of the GCP Service Account Credentials file.
There are multiple techniques to address these three problems, but this article elaborates on a solution using the Firebase account. Firebase is a platform developed by Google for creating a web application and can be readily connected to GCP projects.
Current application
- Anyone can access the App Engine instance from URL;
- The App Engine instance owns a GCP Service Account Credentials file (e.g.
.json
); - Therefore, the App Engine instance creates
jwt
as an authentication token for the Cloud Endpoint; - Consequently, anyone can have an access to the Cloud Run instance from the App Engine instance.
Updated application
- The authentication process is required to access the App Engine instance from URL (based on Firebase);
- The App Engine instance does not own a GCP Service Account Credentials file;
- The App Engine instance enables to generate
id_token
using the Firebase SDK, NOT using a GCP Service Account Credentials file; - The Cloud Endpoint instance accepts the
id_token
, generated based on the Firebase SDK. NOTE: theid_token
based on the Firebase pertains tojwt
formats. - Consequently, anyone, who can have an access to the App Engine instance (in other words, verified users of the web application), is able to talk to the Cloud Run instance.
Action plans for the updated application
- Set up Firebase account;
- Create a web application on Firebase;
- Update the Dash application adding Firebase login functionality;
- Update the Cloud Endpoint instance to accept
id_token
, generated by the Firebase SDK.
Step 4: Set up Firebase account
From the GCP console, go to the Firebase console and “start with your GCP project”.
Once the Firebase account is linked to the GCP project, create a user account from the Authentication menu on the left tab. In this example, the identifier
is the user’s email address.
Step 5: Create a web application on Firebase
Next, we create a web application on Firebase, and we are going to connect this web application to our Dash application, using Firebase SDK. The step-by-step official documentation can be found from the link.
First, create a web application from Project Overview.
Register the application with a nickname.
Once the application is created, from the main page, select the application and see Settings.
From Settings, we are able to see Firebase SDK snippet for the web application. We need to use this HTML code snippet for our Dash application.
Step 6: Update the Dash application using the HTML snippet
In order to include the Firebase SDK snippet on our Dash application, for convenience, we split the main dash code into two files.
File 1: app.py
app.py
initialises Dash application, and includes the Firebase SDK snippet. In short, we would like to create a login page before the main function. To this end, we utilise the idea of Customising Dash’s HTML Index Template to modify the default HTML Index Template. (In app.py
example, the idea starts from line 25: app.index_string
)
Note: My JavaScript, CSS, HTML knowledge is not as proficient as my Python. Useful links for adding the Firebase SDK snippet on Dash is below:
- An official example on GitHub,
GoogleCloudPlatform/python-docs-samples
- Authenticate with Firebase using Password-Based Accounts using javaScript
- Authenticate with Firebase Anonymously Using JavaScript
- Easily add sign-in to your Web app with FirebaseUI
File 2: main_v3.py
The differences from the previous Dash application (main_v2.py
) are below:
- The main
html.Div
is named asmain_app_div
, which is not visible (style={"display": "none"}
) by default; - Once the login is successful (i.e. using Firebase authentication), we are able to fetch
id_token
; - A new callback and
login_process
check whether we are able to fetchid_token
from cookies. When we can fetchid_token
, themain_app_div
become visible ({"display": "Block"}
); - The
id_token
will be used asjwt
for an HTTPS request to Cloud Endpoint (we will configure the Cloud Endpoint instance later). - Therefore, the GCP Service Account Credentials file is not needed.
Note: For simplicity and convenience, the example only focuses on adding Firebase SDK snippet on the Dash application. There are many sophisticated techniques to make the application robust (e.g. using session cookies
etc.).
Step 7: Deploy the updated Dash application on App Engine
The updated application needs these five files:
- The main python script for Dash (
main_v3.py
); - A supplemental python file to configure Firebase SDK (
app.py
); - Configuration file (
config_appengine_v3.yaml
); - Docker related files (
Dockerfile, requirements.txt
)
dashapp-ae-v3
├── main_v3.py # Main python file
├── app.py # Sub file including HTML snippet
├── config_appengine_v3.yaml # Config file for App Engine
├── requirements.txt # List of python packages
└── Dockerfile # For Docker build
Note: There are some minor changes in
config_appengine_v3.yaml
: Change theentrypoint:
andservice:
Dockerfile
: Change the last lineCMD python main_v3.py
# config_appengine_v3.yaml# Run by Python 3.7
runtime: python37# Machine config https://cloud.google.com/appengine/docs/standard
instance_class: B2
manual_scaling:
instances: 1# Timeout is 60sec
entrypoint: gunicorn -b :$PORT main_v3:app.server --timeout 60 --workers 1# Instance name
service: dashapp-ae-v3
handlers:
- url: /.*
script: auto
secure: always
Once all of five files are saved in the same directory, deploy the application on App Engine.
$ gcloud app deploy --project=$MY_PROJECT_ID config_appengine_v3.yaml
Go to the App Engine tab from the GCP console again. Click the new instance dashapp-ae-v3
and check whether the Dash application is successfully deployed or not.
If successful, we are not able to see the main application page, but a login window. In other words, we need to login first before we use the application. This authentication is based on the Firebase account.
Once we fill the registered email address and password on the window, we are able to see the main window, where we are able to create an HTTPS request to the Cloud Endpoint instance.
However, since we have not configured the Cloud Endpoint instance yet, the id_token
generated by App Engine is not yet authorised by the Cloud Endpoint instance. Consequently, the message from the instance is {"message":"Jwt issuer is not configured","code":401}
.
Step 8: Update Cloud Endpoint to accept Firebase user accounts
As well as my previous article, we shall configure the Cloud Endpoint instance, which authenticates users based on different methods. Previously, we configured the authentication method using a GCP Service Account Credentials.
To support the Firebase authentication, we need to configure .yaml
file again. The official documentation can be found from the link.
The basic structure of the .yaml
file is the same as the previous example. We need to add firebase
parameters under securityDefinitions
and security
parameters for paths
.
Once the configuration file for Cloud Endpoint is updated, re-deploy two Cloud Run instances (i.e. one for the Flask application, the other of the Cloud Endpoint). The series of actions are summarised in Step 3: Add authentication endpoint on Cloud Run in my previous article.
Step 9: Check the Cloud Endpoint and the Dash application on App Engine
It is time to check the application. To begin with, we need to change the URL on main_v3.py
to send an HTTPS request to the updated Cloud Endpoint.
endpoint_url = "https://flaskapp-cr-v3-gateway-yerjarnciq-ue.a.run.app/"
Once re-deployed our Dash application on App Engine, we shall log in to the App Engine instance again and see the response from the Cloud Endpoint instance.
The id_token
generated by the App Engine instance based on Firebase is now authorised by Cloud Endpoint. Well done!
Consequently, we don’t need to deploy instances with a GCP Service Account Credentials file together to create jwt
.
Conclusion
The authentication based on Firebase is more elegant and easy to maintain multiple users. Embedding the Firebase SDK on the Dash application was challenging for me at first. Hope this article mitigates the technical difficulties and helps someone who is interested in deploying a multi-user web application on Cloud Run and App Engine.