Implementing Streamlit-Authenticator Across Multi-Page Apps

Author:Murphy  |  View: 29266  |  Time: 2025-03-22 19:31:37

Introduction

Streamlit is a widely used tool for creating quick web applications backed by data, but one of the capabilities it does not have is the ability to manage multiple users and identify them while they use the app.

Streamlit has some basic documentation on how you can add this by using hard coded key-value pairs in your secrets.toml file, however this is a really simplistic method and you'll probably outgrow it quickly!

Fortunately, developer Mohammad Khorasani wrote an excellent package that adds pretty sophisticated authentication in a very simple and elegant way! It's called Streamlit Authenticator and is available on GitHub and can be installed with your favorite package manager, such as PIP.

Mohammad supplies a nice sample application and code for a single-page application, but there is not one for a multi-page application. I decided to make a starting point for anyone that is interested in using it. I also checked with the author to make sure the code was correct.

Here is the complete code for your starter application if you want to dive right in.

Image by Author

Challenges with Multi-Page Apps

The main issue you'll find with Multi-Page applications is that you need to properly manage the Streamlit Session State. After you log in, you need to persist the authentication information properly, check to make sure that the user is logged in, and in turn present the correct information.

Please remember to pass the authenticator object to each and every page in a multi-page application as a session state variable.

There are a few notes in the documentation, such as the above, about multi-page apps but not really enough to easily get you up-and-running.

Setting Up the Initial Authenticator

Start by creating the Home.py file that will be the application entry point. We need our standard imports and some basic code to rough out the Streamlit app.

import streamlit as st
import streamlit_authenticator as stauth
from streamlit_authenticator.utilities import LoginError
import yaml
from yaml.loader import SafeLoader

st.title("Streamlit-Authenticator")

Next, per the setup guide, we can add the required information to read the config.yaml file (see the docs on how this all works).

Python"># Load credentials from the YAML file
with open("config.yaml") as file:
     config = yaml.load(file, Loader=SafeLoader)

# Initialize the authenticator
authenticator = stauth.Authenticate(
     config["credentials"],
     config["cookie"]["name"],
     config["cookie"]["key"],
     config["cookie"]["expiry_days"],)

And next, we need to set up the session state per the quote above from the documentation. Here is how we accomplish this.

# Store the authenticator object in the session state
st.session_state["authenticator"] = authenticator
# Store the config in the session state so it can be updated later
st.session_state["config"] = config

Notice that in addition to the authenticator object, we're storing the config that we read from the file above. This allows us to write that out at a later time if and when the config file has been updated, such as changing a password. Finally, we render the login widget and add logic to determine if the user is logged in or not.

I want to also point out the logout button being rendered in the sidebar when the user is logged in. The important step here is to make sure the key= has a unique name for every page. Keep this simple and use your app name and append the page name to the key's value. This is true for the login widget as well.

Unique key provided to widget to avoid duplicate WidgetID errors.

# Authentication logic
try:
    authenticator.login(location="main", key="login-demo-app-home")
except LoginError as e:
     st.error(e)

if st.session_state["authentication_status"]:
     authenticator.logout(location="sidebar", key="logout-demo-app-home")
elif st.session_state["authentication_status"] is False:
     st.error("Username/password is incorrect")
elif st.session_state["authentication_status"] is None:
     st.warning("Please enter your username and password")

Let's also look at what the session state looks like now that we're logged in and have saved the above into session state. We have a few things right away that stand out. There is a key called logout-demo-app-home which represents the state of the logout button. There is our authenticator object stored as well as the config. Additionally, we have access to information about the user such as username, email, name, and roles. All of these are very useful for personalizing your application!

Image by Author

Setting Up all Additional Pages

Now for the good part: Getting this to work seamlessly on subsequent pages in your application. Create a new file in a folder called pages and name it it appropriately for your functionality. I'll keep this simple and name it Page_1.py. Let's start with our imports and basic Streamlit code. Notice that we don't have to directly import any of the Authenticator packages since we're only going to be using the session state's information.

import streamlit as st
import yaml

st.set_page_config(page_title="Page 1")
st.title("Page 1")

And then before we render any page information, we can check to see if the user is authenticated. There is a top level key in the session state called authentication_status which is a boolean. We can simple check to see if is is true, and in turn retrieve the authenticator object from the session state.

One of the tips the developer has is that for each page you need to call the login widget, but this time without rendering it. You additionally will give this a new, unique key-value that will help with session collisions as you navigate pages.

if st.session_state.get("authentication_status"):
    authenticator = st.session_state.get("authenticator")
    authenticator.logout(location="sidebar", key="logout-demo-app-page-1")
    authenticator.login(location="unrendered", key="authenticator-page-1")
    # Put the main code and logic for your page here.
    st.success("You are logged in!")

elif st.session_state == {} or st.session_state["authentication_status"] is None:
    st.warning("Please use the button below to navigate to Home and log in.")
    st.page_link("Home.py", label="Home", icon="         

Tags: App Development Data Science Programming Python Streamlit

Comment