Implement user login and multi-page view using Pyrebase

Hello everyone

Am getting stumped on trying to implement a multi-user login solution using Pyrebase.

I have a working copy of the code for a single-user password-protected login, unfortunately am unable to implement a multi-user password-protected login using Pyrebase.

I organized my code as follows:

main.py: A list of Streamlit apps/pages in the sidebar for users to navigate

import streams
import streamlit as st


def main():

    PAGES = {
        "Stream One": streams.one,
        "Stream Two": streams.two,
        "Stream Three": streams.three
    }

    st.sidebar.title("My Streamlit App")
    selection = st.sidebar.radio("Go to", list(PAGES.keys()))

    PAGES[selection].write()

streams.one,two,three are .py files that contain a .write() method that are stand-alone Streamlit scripts.


multi_user_auth.py: Authenticates the user before allowing view access to the pages in main.py

import streams
import pyrebase
import streamlit as st
from streams.main import main


def authenticate():

    # Configuration Key
    firebaseConfig = {
        ...
    };

    # Firebase Authentication
    firebase = pyrebase.initialize_app(firebaseConfig)
    auth = firebase.auth()

    # Database
    db = firebase.database()
    storage = firebase.storage()

    # Authentication
    choice_block = st.empty()
    choice = choice_block.radio('Login / Signup', ['Login', 'Sign up'])

    # E-mail input
    email_block = st.empty()
    email = email_block.text_input('Please enter your email address')

    # Password input
    password_block = st.empty()
    password = password_block.text_input('Please enter your password',type = 'password')

    # Sign up block
    if choice == 'Sign up':
        username_block = st.empty()
        username = username_block.text_input('Please input your app user name', value='Default')

        sign_up_block = st.empty()
        sign_up = sign_up_block.button('Sign Up')

        if sign_up:
            try:
                user = auth.create_user_with_email_and_password(email, password)
                st.success('Your account was created successfully!')
                
                # Sign in
                user = auth.sign_in_with_email_and_password(email, password)
                db.child(user['localId']).child("Username").set(username)
                db.child(user['localId']).child("ID").set(user['localId'])
                st.info(f"Welcome {username}, you can now login to the system.")
                sign_up_block.empty()

            except:
                st.error('Account already exists!')

    # Login block
    elif choice == 'Login':

        # Login button
        login_block = st.empty()
        login = login_block.button('Login')

        if login:
            user = auth.sign_in_with_email_and_password(email,password)
            while user:
                username = db.child(user['localId']).child("Username").get().val()
                st.sidebar.title(f"Welcome {username}!")

                # Delete all placeholders
                choice_block.empty()
                email_block.empty()
                password_block.empty()
                login_block.empty()

                # Display all pages to the user for navigation
                main()

if __name__ == "__main__":
    authenticate()

After the user signs up and logs in, all pages defined in main.py display successfully in the sidebar.

However, if the user clicks on any of the other pages in the sidebar, instead of calling the write() method on that particular page, Streamlit re-directs to the login page again, forcing the user to re-input their login details.

Is there any way to preserve / persist session state such that the user can navigate through the multiple pages in the sidebar once logged in?

I managed to get this behavior working using st.session_state and a single password input (script below), but this does not cater to the desired outcome of multi-user password login.

single_password_auth.py: Controls access to pages using a single password, but cannot cater to multi-user login

import streamlit as st
from streams.main import main

def authenticate():

    # Initialize an empty session state variable
    if 'password' not in st.session_state:
        st.session_state['password'] = ''

    # Check if password matches input
    if st.session_state.password != 'SuperSecretPassword!':
        login_block = st.empty()
        password = login_block.text_input('Please enter the password:', value='', type='password')
        st.session_state.password = password
        
        if st.session_state.password == 'SuperSecretPassword!':
            # Remove title after user has logged in
            login_block.empty()
            title.empty()
            main()
        
        elif password:
            st.error("Please enter a valid password")

    else: 
        title.empty()
        main()

if __name__ == "__main__":
    authenticate(main)

In case anyone is looking for a multi-page app with multi-user authentication, can try out hydralit

If you are not using hydralit, to keep the user logged in, use checbox instead of button for the login component.