Streamlit Authentication
Streamlit is an open-source Python library that allows you to create and share beautiful, interactive data applications and dashboards with minimal code. Streamlit is particularly useful for creating data science and machine learning applications, dashboards, and data visualization tools.
That being said, out-of-the-box authentication options with Streamlit are limited. That's where PropelAuth comes in. This guide will cover how to build and deploy a Streamlit app using PropelAuth for authentication. We'll be using PropelAuth's OAuth2 support alongside Streamlit's OIDC integration.
Setup in PropelAuth
We first need to create an OAuth2 provider within PropelAuth. To do so, navigate to the Frontend Integration page in your PropelAuth dashboard, click on Advanced Settings then Edit OAuth Config.
Create a New OAuth Client and copy down your Client ID and Client Secret. We'll be needing these for later.
While we're here, let's also add a Redirect URI. Streamlit requires that this value is equal to your app's absolute URL with the pathname oauth2callback
. In this guide we'll be using http://localhost:8501/oauth2callback
.
The last variable we need is our Auth URL. You can find this back in the Frontend Integration page.
Installation and Initialization
Let's begin by navigating to (or creating if you haven't done so already) your Streamit app's secrets.toml
file. Here, we'll use the Auth URL, Redirect URI, Client ID, and Client Secret that we retrieved in the previous step.
You'll also need to create a cookie_secret
which is a randomly generated string. Here's Streamlit's documentation on it.
.streamlit/secrets.toml
[auth]
redirect_uri = "{YOUR_REDIRECT_URI}" # "http://localhost:8501/oauth2callback"
cookie_secret = "{RANDOMLY_GENERATED_STRING}"
client_id = "{YOUR_CLIENT_ID}"
client_secret = "{YOUR_CLIENT_SECRET}"
server_metadata_url = "{YOUR_AUTH_URL}/.well-known/openid-configuration"
client_kwargs = { "prompt" = "login" } # Optional.
# Will force the user to go through the login flow.
In your Streamlit project, install the propelauth-py library:
pip install propelauth_py
Now create a new file in your root directory and name it propelauth.py
. Copy the following code into it, making sure to edit YOUR_AUTH_URL
and YOUR_API_KEY
with the values found in your Backend Integration page in PropelAuth. You likely want to load them as environment variables instead of hardcoding them.
propelauth.py
import streamlit as st
from propelauth_py import init_base_auth
# Replace me
AUTH_URL = "YOUR_AUTH_URL"
API_KEY = "YOUR_API_KEY"
class Auth:
def __init__(self, auth_url, integration_api_key):
self.auth = init_base_auth(auth_url, integration_api_key)
self.auth_url = auth_url
self.integration_api_key = integration_api_key
self.access_token = None
def get_user(self, user_id):
try:
if self.access_token is None:
return self.force_refresh_user(user_id)
return self.auth.validate_access_token_and_get_user(f"Bearer {self.access_token}")
except UnauthorizedException:
return self.force_refresh_user(user_id)
def force_refresh_user(self, user_id):
access_token_response = self.auth.create_access_token(user_id, 10)
self.access_token = access_token_response.access_token
return self.auth.validate_access_token_and_get_user(f"Bearer {self.access_token}")
def get_account_url(self):
return self.auth_url + "/account"
def log_out(self, user_id):
self.auth.logout_all_user_sessions(user_id)
self.access_token = None
st.logout()
auth = Auth(
AUTH_URL,
API_KEY
)
This is a helper class which will help retrieve the user's information, redirect to the user's hosted account page, and log the user out. We'll cover some of these functions in this guide, but feel free to use these functions or add more yourself!
And we're all setup! Let's move on to protecting your Streamlit pages as well as displaying the logged in user's information.
Protecting Pages And Displaying User information
Before we move on, it's important to understand the difference between Streamlit's st.experimental_user and the User object we get back from the function get_user
found in our propelauth.py
script.
The st.experimental_user
is the user object that is returned from Streamlit after the user authenticates successfully. It's missing critical information about the user, such as which organizations they are members of as well as any custom user properties they may have. On the other hand, the user returned from get_user
will include all of this information as well as helper functions such as get_orgs
and is_role_in_org
.
That being said, st.experimental_user
is very good at checking if the user is logged in or not. In this example, we'll use st.experimental_user
to check if the user is authenticated. If not, we'll redirect the user to login via PropelAuth using Streamlit's st.login command.
import streamlit as st
from propelauth import auth
if st.experimental_user.is_logged_in:
user = auth.get_user(st.experimental_user.sub)
if user:
st.write(user)
else:
st.write("You are not logged in.")
st.button("Login", on_click=st.login)
st.stop()
Above, we first check if the user is authenticated. If the user is not logged in, we'll display a Login button like so:
If the user is logged in, we'll fetch the full User object using get_user
and then display the returned information.
Authorization / organizations
You can also verify which organizations the user is in, and which roles and permissions they have in each organization using the User object.
Check Org Membership
Verify that the user is logged in and that the user is a member of the specified organization.
def return_org_name(org):
return org.org_name
if st.experimental_user.is_logged_in:
user = auth.get_user(st.experimental_user.sub)
org = st.selectbox("Select an organization", user.get_orgs(), format_func=return_org_name)
if org:
st.write(f"You are in org {org.org_id}")
else:
st.write("You do not belong to this org")
Check Org Membership and Role
Similar to checking org membership, but will also verify that the user has a specific Role in the organization. This can be done using either the User or OrgMemberInfo objects.
A user has a Role within an organization. By default, the available roles are Owner, Admin, or Member, but these can be configured. These roles are also hierarchical, so Owner > Admin > Member.
def return_org_name(org):
return org.org_name
if st.experimental_user.is_logged_in:
user = auth.get_user(st.experimental_user.sub)
org = st.selectbox("Select an organization", user.get_orgs(), format_func=return_org_name)
role_to_check = "Admin"
if org and org.user_is_role(role_to_check):
st.write(f"You are an Admin in org {org.org_id}")
else:
st.write("You must be an Admin to access this page")
Check Org Membership and Permission
Similar to checking org membership, but will also verify that the user has the specified permission in the organization. This can be done using either the User or OrgMemberInfo objects.
Permissions are arbitrary strings associated with a role. For example, can_view_billing
, ProductA::CanCreate
, and ReadOnly
are all valid permissions. You can create these permissions in the PropelAuth dashboard.
def return_org_name(org):
return org.org_name
if st.experimental_user.is_logged_in:
user = auth.get_user(st.experimental_user.sub)
org = st.selectbox("Select an organization", user.get_orgs(), format_func=return_org_name)
permission_to_check = "can_view_billing"
if org and org.user_has_permission(permission_to_check):
st.write(f"You can view billing information for org {org.org_id}")
else:
st.write("You do not have the necessary permission to view billing information.")
Calling Backend APIs
As you may have noticed, this guide uses PropelAuth's propelauth-py library to retrieve the user's information, among other things. This means you can use the propelauth-py
library to make requests to PropelAuth's Backend API, allowing you to fetch members of an organization, create orgs, and a lot more.
from propelauth import auth
magic_link = auth.auth.create_magic_link(email="test@example.com")
Logging Out
To log a user out of your Streamlit app you can use the log_out
function found in your propelauth.py
file.
import streamlit as st
from propelauth import auth
if st.experimental_user.is_logged_in:
if st.button("Logout"):
auth.log_out(st.experimental_user.sub)
Logging Out Through The Hosted Pages
If you're using PropelAuth's hosted pages you may find that the Log Out button found in the Account Page Sidebar does not completely log the user out of their Streamlit session.
Removing the Log Out Button
There are a couple of ways to address this. First, you can remove the Sidebar by navigating to the Look & Feel section of your PropelAuth Dashboard, clicking Open Editor followed by Management Pages towards the top.
Find the Display Sidebar setting and disable it.
The Sidebar as well as the Logout button are now removed from the hosted pages.
Creating a Logout Page
If you would like to keep the Log Out button, we recommend creating a page in your Streamlit app that will automatically log users out when redirected to. Let's create a page that is located at /logout
:
import streamlit as st
from propelauth import auth
if st.experimental_user.is_logged_in:
auth.log_out(st.experimental_user.sub)
st.switch_page("home.py") # redirect back to your home page
If a logged in user were to visit this page they'll be logged out and then redirected back to your home page. The second step here is to set your Default redirect path after logout setting, found in your Frontend Integrations page in the PropelAuth Dashboard. This is the path where users will be redirected to after clicking the Log Out button found in the hosted pages.
You can set this to /logout
, or wherever you decided to place your logout page. Now when the user clicks the Log Out button, they'll first be logged out of the hosted pages, redirected to the /logout
page of your application where they'll be logged out of Streamlit, and then redirected back to your home page.
Have any questions? Please reach out to support@propelauth.com.