St.session_state deletes on refresh

Hey guys, I am trying to build a login wall with Google OAuth.
When logging the user for the first time I store the OAuth token in st.session_state.token.

However, every time I refresh the page, the session_state gets erased and throws the client out of the page.

Here is the code I am using streamlit-google-oauth/app.py at main Ā· uiucanh/streamlit-google-oauth Ā· GitHub.

There is also a problem with write_access_token() function in the code because it always returns an ā€œmalformed auth codeā€ error.
But still, even if it worked it would refresh the session_state anyway.

Is there a way to set an expiration time for session_state? How do you keep your users logged in after they refresh the page?

Thanks :pray:

Hi Daniel,

Did you resolved you issue? I got same problem with you, Iā€™m building a simple login page now, my example is very simple, just require user to input a password (no username), then I stored this password in session, if user visit next time or the page get refreshed, I will get that password from session to verify the request, but each time the page refreshed, the session dicta is empty.

The app posted by @danielhangan appears to be using a custom session_state object. Try using the native st.session_state.

Arvindra

Thanks! but still not working, here is my code, if I refresh the page, it always print a empty dict:

import streamlit as st

print(st.session_state)

if 'password' in st.session_state:
    st.success('Good')
else:
    password = st.text_input('Password')
    confirm = st.checkbox('Confirm')
    if confirm:
        if password == '123':
            st.session_state.password = password
            st.success('Good')
        else:
            st.warning('Try again!')

What I want is, unless user closed his browser otherwise user will no need to input the password again.

The session is tied to the socket connection so when you refresh the browser the socket is killed and renewed. Youā€™d could use a database to store your token, or try wrapping your token state update and retrieval in st.experimental_memo with disk persistence.

Iā€™ve seen a cookies implementation in the forum, that may also help to persist the token.

Iā€™ve published a couple of login solutions too that may help (one simple using a database and another more about identities using Auth0). Just see my posts.

1 Like

@ScottZhang

So, what I ended up doing is setting a param in the url everytime the user logs in and only deleting it when he logs out. Url params remain the same on page refresh.

Login: st.experimental_set_query_params(code=ā€œlogged_inā€) everytime the user logs in.
Check Login: st.experimental_get_query_params()[ā€˜codeā€™][0]
Logout: st.experimental_set_query_params(code=ā€œlogged_outā€)

Here is my code.

main.py

def main(user: object):
    st.write(f"You're logged in as {st.session_state['user']['email']}")

    set_code(code=user['refreshToken'])

    st.write("Hello World")

app.py

if __name__ == '__main__':

    # firebase configuration
    firebaseConfig = {
        "apiKey": os.environ['APIKEY'],
        "authDomain": os.environ['AUTHDOMAIN'],
        "databaseURL": os.environ['DATABASEURL'],
        "projectId": os.environ['PROJECTID'],
        "storageBucket": os.environ['STORAGEBUCKET'],
    }

    firebase = pyrebase.initialize_app(firebaseConfig)

    auth = firebase.auth()


    # authentification
    if "user" not in st.session_state:
        st.session_state['user'] = None

    if st.session_state['user'] is None:
        try:
            code = st.experimental_get_query_params()['code'][0]

            refreshToken = refresh_session_token(auth=auth, code=code)

            if refreshToken == 'fail to refresh':
                raise(ValueError)

            user = get_user_token(auth, refreshToken=refreshToken)

            main(user=user)
        except:
            st.title("Login")
            login_form(auth)

    else:
        main(user=st.session_state['user'])

auth.py

import streamlit as st
import requests


def set_code(code: str):
    st.experimental_set_query_params(code=code)


def login_form(auth):
    email = st.text_input(
        label="email", placeholder="fullname@gmail.com")
    password = st.text_input(
        label="password", placeholder="password", type="password")

    if st.button("login"):
        try:
            user = auth.sign_in_with_email_and_password(email, password)
            st.session_state['user'] = user
            st.experimental_rerun()
        except requests.HTTPError as exception:
            st.write(exception)


def logout():
    del st.session_state['user']
    st.experimental_set_query_params(code="/logout")


def get_user_token(auth, refreshToken: object):
    user = auth.get_account_info(refreshToken['idToken'])

    user = {
        "email": user['users'][0]['email'],
        "refreshToken": refreshToken['refreshToken'],
        "idToken": refreshToken['idToken']
    }

    st.session_state['user'] = user

    return user


def refresh_session_token(auth, code: str):
    try:
        return auth.refresh(code)
    except:
        return "fail to refresh"

Its not ideal. But works for the time being.

Points for improvement.

  • check if token expired.
6 Likes

Great you got it workingā€¦ highlights that session state is nice, but certainly not resilient and there are cases where it just canā€™t be used (as expected with a pure web app session stateā€¦ Web app sessions (cookies, more specifically) and Streamlit app sessions are not equal).

A

1 Like

would be great to have st.session_state be preserved between browser reloads. Otheriwse it should be called rerun_state

4 Likes

@danielhangan Does this mean if the user shares the URL to the streamlit app with someone and accidentally copies the query parameters, the new user will be able to log in on their behalf?

@mansidak Yes.

Gotchu. Thanks! I was able achieve something similar but I used cookies instead of query-parameter to be session-safe. Quick question: do you know if thereā€™s something wrong with moving the main() function to the if __name__ == '__main__': block or should you always have it outside?

should be no problem. I have it outside to keep the code clean and readable.

Can you share how you implemented the cookie functionality? Is there a particular topic on the forum I should have a look at?

1 Like

In my case, I get and AWS credentials through argparse and create two st.session_state in the home.py, expecting to use them in other pages. Unfortunatelly I get the same problems mentioned previously. My suggestion would be to re-launch streamlit the same way it was launched initially (with the same arguments used in the original execution. Would that make the session_state vars be correctly initialized and reusable across all pages?