Playing with multiple ways of authenticating through a Google account within Streamlit:
- Decomposing server-side OAuth flow through Google Auth Oauthlib and FastAPI to catch redirect URIs and manage session cookies
- Google Signin Button in a Streamlit Component
- Firebase Authentication with Firebase UI in a Streamlit Component
- A Google Project. Initialize one from the GCP Console
- A Firebase Project. To keep things tidy, create a Firebase project over the previously created GCP Project through the Firebase Console
- For practice's sake, enable local HTTPS for your Streamlit app
- I ran
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
in WSL2 on Windows. - Then moved
cert.pem
andkey.pem
to.streamlit
folder at root of project - Finally added
sslCertFile = ".streamlit/cert.pem"
andsslKeyFile = ".streamlit/key.pem"
to.streamlit/config.toml
- I ran
[server]
sslCertFile = ".streamlit/cert.pem"
sslKeyFile = ".streamlit/key.pem"
Better use Caddy or Nginx to SSL proxify Streamlit & FastAPI
Head to GCP Console
-
In APIs and services > OAuth consent screen:
- configure email, links on first page.
- in Scopes second page, add OpenID Connect scopes:
openid
,../auth/userinfo.profile
and.../auth/userinfo.email
.- If you enabled the
Calendar API
(either by typing it in search bar or from APIs and services >Enabled APIs and Services), add the.../auth/calendar.events.readonly
to ask the user for Calendar authorization
- If you enabled the
-
add tests users in 3rd page
-
In APIs and services > Credentials, create a OAuth 2.0 Web Client ID and download it as a JSON file
client_secret.json
to the root of the project.- As authorised Javascript origins, I added the local Streamlit URL:
https://localhost:8501
- As authorised redirect URIs, I added:
http://localhost:9000/
: Flask endpoint from google-oauthlib'sInstalledAppFlow.run_local_server
andget_user_credentials
methodshttp://localhost:8000/auth/code
andhttp://localhost:8000/auth/token
: our own FastAPI callback endpoints
- As authorised Javascript origins, I added the local Streamlit URL:
- Copy the credentials
client_id
andclient_secret
to.streamlit/secrets.toml
file
client_id="XXX.apps.googleusercontent.com"
client_secret="XXXXXX-..."
Next, head to Firebase Console
- In Firebase Authentication > Sign-in method, enable Google Provider.
- In Project Overview > Project Settings > Service Acctouns, generate a new private key and download JSON file as
firebase_secret.json
to the root of the project. - In Project Overview, click the
Add app
button, and add aWeb
App. After creation, copy thefirebaseConfig
variable into afirebase_client.json
file at the root of the project.
Install Python packages: pip install -r requirements.txt
Run Streamlit multipage app: streamlit run streamlit_app.py
- Access Streamlit app in
https://localhost:8501
For pages 3 and 4, run FastAPI redirect server in parallel: fastapi dev fastapi_server.py
.
- Access FastAPI OpenAPI in
http://localhost:8000/docs
. Visualize state of app in/sessions
URI.
- Good enough for local / on-premise deployments
- Wouldn't work on Streamlit Cloud because the Flask port is not open. Create your own Docker image to expose Streamlit + Flask ports
- Deploy FastAPI separately as authentication and session cookie management service
- Hide both services behind reverse proxy with single URL
- I think it's a difficult setup to maintain. FastAPI + DB session cookies to replace with Firebase Authentication or Auth0 if you don't have time for this like me...
- When Native OAuth+OIDC, redirects and custom endpoints appear in Streamlit, this solution seems the best
- Streamlit Component which embeds Google / Firebase signin
- Google signin stores SIDCC cookie, Firebase uses API Keys+JWT in IndexedDB on browser to track user session
- Because of Streamlit Component iframe embed, doesn't work in deployments because CSRF issues. Streamlit host and iframe embedding signin have different addresses.
- Google Tap has intermediate Iframe that may solve this?