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.

@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.
1 Like

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