Login button random behavior

The problem I’m facing is pertaining to the login button.
The issue is that when user logs in with right credential, then logs out, then logs in again.
In this second login, the user has to click the login button twice.

I don’t know what the issue might be. Has to be with session states but I thought they are persistent even in multi-page app.

Also, as a sidenote, sometimes in the first try when logging in, I get this faulty behavior and need to click again. What might be the issue?

Below is a small sample of the full code.

(My full code can be found at this Github Repository: GitHub - yashmehtakristal/KristalGPT-test: testing the authentication of KristalGPT.

This is my mainapp.py:

# All imports
__import__('pysqlite3')
import sys
sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')

import streamlit as st

# Setting page config & header
st.set_page_config(page_title="Kristal Retriever", page_icon="πŸ“–", layout="wide", initial_sidebar_state="expanded")
st.header("πŸ“– Kristal Retriever")

import openai
import os
import tempfile
from tempfile import NamedTemporaryFile
from streamlit_extras.app_logo import add_logo
from st_pages import Page, Section, add_page_title, show_pages, hide_pages
from database_helper_functions import sign_up, fetch_users
import streamlit_authenticator as stauth


# Add the logo to the sidebar
add_logo("https://assets-global.website-files.com/614a9edd8139f5def3897a73/61960dbb839ce5fefe853138_Kristal%20Logotype%20Primary.svg")

show_pages(
    [
        Page("main.py","Login", "πŸ—οΈ"),
        Page("pages/home.py", "About", "πŸ˜€"),
        # Section(name = "Bulk Upload", icon="πŸ“š"),
        Page("pages/bulk_upload_basic.py", "Bulk Upload - Basic", "πŸ“š"),
        Page("pages/bulk_upload_advanced.py", "Bulk Upload - Advanced", "πŸ“š"),
        # Section(name = "QA Basic", icon="❓"),
        Page("pages/qa_basic.py", "Q&A - Basic", "❓"),
        Page("pages/qa_advanced.py", "Q&A - Advanced", "❓"),
        # Section(name = "Chatbot", icon="πŸ’¬"),
        # Page("pages/chatbot_without_memory.py", "Chatbot - Basic", "πŸ’¬"),
        # Page("pages/chatbot_with_memory.py", "Chatbot - Advanced", "πŸ’¬")
    ]
)

# Session state variable to save "logged out" boolean value
if "logged_out" not in st.session_state:
    st.session_state.logged_out = False

# Session state variable to save "logged in" boolean value
if "logged_in" not in st.session_state:
    st.session_state.logged_in = False

# st.session_state['authentication_status']
if "authentication_status" not in st.session_state:
    st.session_state.authentication_status = False


# Hide particular pages if not logged in
if not st.session_state.logged_in:
    hide_pages(["About", "Bulk Upload - Basic", "Bulk Upload - Advanced", "Q&A - Basic", "Q&A - Advanced"])

# Hide particular pages if logged out
if st.session_state.logged_out:
    hide_pages(["About", "Bulk Upload - Basic", "Bulk Upload - Advanced", "Q&A - Basic", "Q&A - Advanced"])


# Session state variable to save "username"
if "username" not in st.session_state:
    st.session_state.username = ''

# Session state variable to save "Authenticator" object
if "Authenticator" not in st.session_state:
    st.session_state.Authenticator = None

# Session state variable to save "Logout" object
if "logout" not in st.session_state:
    st.session_state.logout = False

if "authentication_status" not in st.session_state:
    st.session_state.authentication_status = False

if "credentials" not in st.session_state:
    st.session_state.credentials = {}


# Calling the fetch_users() function which returns a dictionary of users
users = fetch_users()

# Will store the respective keys in the dictionary in the following lists
emails = []
usernames = []
passwords = []

for user in users:
    emails.append(user['key'])
    usernames.append(user['username'])
    passwords.append(user['password'])

# Storing the credentials for each user in a dictionary
credentials = {'usernames': {}}

for index in range(len(emails)):
    credentials['usernames'][usernames[index]] = {'name': emails[index], 'password': passwords[index]}

st.session_state.credentials = credentials

# Create an authentication object of the credentials
# Along, with the name of the cookie (to reauthenticate user without them re-entering credentials, so they can refresh page without providing their password again)
# Write random key to hash a cookies signature (abcdef)
# Specify number of days cookie can be used for (30 days)
Authenticator = stauth.Authenticate(credentials, cookie_name = 'Streamlit', key = 'abcdef', cookie_expiry_days = 0)
# Authenticator = stauth.Authenticate(credentials, cookie_name = 'Streamlit', key = 'abcdef', cookie_expiry_days = 30)

# Save Authenticator to session state
st.session_state.Authenticator = Authenticator

email, authentication_status, username = Authenticator.login('Login', 'main') # Get the email, authentication status and username from Login module
st.session_state.authentication_status = authentication_status

# st.rerun()

st.write("After authentication")
st.write("Authentication status variable", authentication_status)
st.write("Authentication status session state", st.session_state.authentication_status)

info, info1 = st.columns(2)

# If username is provided
if username:

    # If username in the usernames list (from database)
    if username in usernames:

        # Save the username to session state
        st.session_state.username = username

        # st.rerun()

        # Inside if loop
        st.write("Inside if loop")
        st.write("Authentication status variable", authentication_status)
        st.write("Authentication status session state", st.session_state.authentication_status)


        # If authentication status is True
        # If st.session_state['authentication_status'] is True:
        if authentication_status is True:

            # Setting session state of logged in = True
            # & log out = False
            st.session_state.logged_in = True
            st.session_state.logout = False

            # let User see app
            st.sidebar.subheader(f'Welcome {username}')
            logout_button = Authenticator.logout('Log Out', 'sidebar')

            # If user has clicked logged_out button, update the state variables
            if logout_button:
                st.session_state.logged_out = True
                st.session_state.logged_in = False

        # ERROR HANDLING
        # st.session_state['authentication_status'] is False
        elif authentication_status is False:
            with info:
                st.error('Incorrect Password or username')
        
        else:
            # Inside else loop
            st.write("Inside else loop")
            st.write("Authentication status variable", authentication_status)
            st.write("Authentication status session state", st.session_state.authentication_status)

            with info:
                st.warning('Please feed in your credentials')
    
    else:
        with info:
            st.warning('Username does not exist, Please Sign up')

I have a multi-page app, but let’s take this one page (test.py) for example:

import streamlit as st
from streamlit_extras.app_logo import add_logo
from st_pages import Page, Section, add_page_title, show_pages, hide_pages
import streamlit_authenticator as stauth

# Setting page config & header
st.set_page_config(page_title = "Kristal Retriever", page_icon = "πŸ“–", layout = "wide", initial_sidebar_state = "expanded")
st.header("πŸ“– Kristal Retriever")

# Hide particular pages if not logged in
if not st.session_state.logged_in:
    hide_pages(["Bulk Upload - Basic", "Bulk Upload - Advanced", "Q&A - Basic", "Q&A - Advanced"])

# Hide particular pages if logged out
if st.session_state.logged_out:
    hide_pages(["Bulk Upload - Basic", "Bulk Upload - Advanced", "Q&A - Basic", "Q&A - Advanced"])

# Add the logo to the sidebar
add_logo("https://assets-global.website-files.com/614a9edd8139f5def3897a73/61960dbb839ce5fefe853138_Kristal%20Logotype%20Primary.svg")


import openai
import os
import tempfile
from tempfile import NamedTemporaryFile
from database_helper_functions import sign_up, fetch_users
import streamlit_authenticator as stauth

# let User access app only if logged in = True & logged out = False
if st.session_state.logged_in is True and st.session_state.logout is False:

    # st.write()

    st.sidebar.subheader(f'Welcome {st.session_state.username}')

    st.write(st.session_state.Authenticator)

    #st.session_state.Authenticator.logout('Log Out', 'sidebar')
    Authenticator = stauth.Authenticate(st.session_state.credentials, cookie_name = 'Streamlit', key = 'abcdef', cookie_expiry_days = 0)
    logout_button = Authenticator.logout('Log Out', 'sidebar')

    # If user has clicked logged_out button, update the state variables
    if logout_button:
        st.session_state.logged_out = True
        st.session_state.logged_in = False
        st.session_state.authentication_status = False
        # st.write("Before Rerun")
        # st.write(st.session_state.logged_out, st.session_state.logged_in)
        # st.write("XXXX")

        st.rerun()


    # Display Markdown of the main page
    st.markdown("Markdown page")

else:
    st.write("Authentication status session state", st.session_state.authentication_status)
    st.info("Seems like you are not logged in. Please head over to the Login page to login", icon="ℹ️")

Hi, I have created an even simpler minimal reproducible example. Please check this:

# Need to import pysqlite3 like this
__import__('pysqlite3')
import sys
sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')

import streamlit as st

# Setting page config & header
st.set_page_config(page_title="Kristal Retriever", page_icon="πŸ“–", layout="wide", initial_sidebar_state="expanded")
st.header("πŸ“– Kristal Retriever")

from streamlit_extras.app_logo import add_logo
from st_pages import Page, Section, add_page_title, show_pages, hide_pages
from database_helper_functions import sign_up, fetch_users
import streamlit_authenticator as stauth
import bcrypt


# Add the logo to the sidebar
add_logo("https://assets-global.website-files.com/614a9edd8139f5def3897a73/61960dbb839ce5fefe853138_Kristal%20Logotype%20Primary.svg")

show_pages(
    [
        Page("main.py","Login", "πŸ—οΈ"),
        Page("pages/home.py", "About", "πŸ˜€"),
        Page("pages/bulk_upload_basic.py", "Bulk Upload - Basic", "πŸ“š"),
        Page("pages/bulk_upload_advanced.py", "Bulk Upload - Advanced", "πŸ“š"),
        Page("pages/qa_basic.py", "Q&A - Basic", "❓"),
        Page("pages/qa_advanced.py", "Q&A - Advanced", "❓")
    ]
)

# Session state variables
if "logged_out" not in st.session_state:
    st.session_state.logged_out = False

if "logged_in" not in st.session_state:
    st.session_state.logged_in = False

if not st.session_state.logged_in:
    hide_pages(["About", "Bulk Upload - Basic", "Bulk Upload - Advanced", "Q&A - Basic", "Q&A - Advanced"])

if st.session_state.logged_out:
    hide_pages(["About", "Bulk Upload - Basic", "Bulk Upload - Advanced", "Q&A - Basic", "Q&A - Advanced"])

if "username" not in st.session_state:
    st.session_state.username = ''

if "Authenticator" not in st.session_state:
    st.session_state.Authenticator = None

if "logout" not in st.session_state:
    st.session_state.logout = False


try:
    users = fetch_users()

    emails = []
    usernames = []
    passwords = []

    for user in users:
        emails.append(user['key'])
        usernames.append(user['username'])
        passwords.append(user['password'])

    credentials = {'usernames': {}}
    
    for index in range(len(emails)):
        credentials['usernames'][usernames[index]] = {'name': emails[index], 'password': passwords[index]}

    Authenticator = stauth.Authenticate(credentials, cookie_name = 'Streamlit', key = 'abcdef', cookie_expiry_days = 0)
    st.session_state.Authenticator = Authenticator

    # email, authentication_status, username = Authenticator.login('Login', 'main')

    
    with st.form(key='login'):
        st.subheader('Login')

        username = st.text_input('Username', placeholder='Enter Your Username', help =
                        '''
                        Please make sure:
                        1) Username is at least 2 characters long
                        2) Username contains only alphanumeric characters (letters and digits)
                        '''
                        )
        
        password = st.text_input('Password', placeholder='Enter Your Password', type='password',
                            help =
                            '''
                            Please make sure:
                            1) Length of password is at least 6 characters long
                            2) Password can contain any characters (letters, digits, underscore, dashes, period etc)
                            '''
                            )
        
        btn1, bt2, btn3, btn4, btn5 = st.columns(5)

        with btn1:
            login_button = st.form_submit_button('Login')


    # Checking if login button is pressed
    if login_button:
        
        info, info1 = st.columns(2)
        
        if username:
            if username in usernames:
                if password:

                    st.session_state.username = username
                    password_match = bcrypt.checkpw(password.encode(), credentials['usernames'][username]['password'].encode())

                    if password_match is True:
                        st.session_state.logged_in = True
                        st.session_state.logout = False

                        st.sidebar.subheader(f'Welcome {username}')
                        logout_button = Authenticator.logout('Log Out', 'sidebar')

                        if logout_button:
                            st.session_state.logged_out = True
                            st.session_state.logged_in = False

                    elif password_match is False:
                        with info:
                            st.error('Incorrect Password or username')

                else:
                    with info:
                        st.warning('Please enter the password field')

            else:
                with info:
                    st.warning('Username does not exist in database')

        else:
            with info:
                st.warning('Please enter the username field')

except:
    st.success('Refresh Page')