Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

st.cookie, or other browser-persistent storage #861

Open
tconkling opened this issue Dec 18, 2019 · 19 comments
Open

st.cookie, or other browser-persistent storage #861

tconkling opened this issue Dec 18, 2019 · 19 comments
Labels
feature:st.user type:enhancement Requests for feature enhancements or new features

Comments

@tconkling
Copy link
Contributor

tconkling commented Dec 18, 2019

In my hackathon project, which was a dashboard that used auth, I really wanted a way to persist an authToken across multiple sessions for the same user.


Community voting on feature requests enables the Streamlit team to understand which features are most important to our users.

If you'd like the Streamlit team to prioritize this feature request, please use the 👍 (thumbs up emoji) reaction in response to the initial post.

@tconkling tconkling added type:enhancement Requests for feature enhancements or new features spec needed labels Dec 18, 2019
@demmerichs
Copy link

I would also love this feature. For the moment, does anyone know a good workaround? Usecase is some manager instance that should be shared across all browser sessions and persist as long as the streamlit app is running. I guess this is something similar to st.cache but with no option of clearing it. (maybe a flag for st.cache named persistent?)

@ZupoLlask
Copy link

Hi guys!

Please prioritize this! 👍😊

@frankhuurman
Copy link

I'm so in need of a solution like this to not have the user enter credentials each time after a refresh. Would love it!
The sessionstate object can only do so much between radio button pages but keeping state after a refresh isn't one of them :)

@KevinLinSL
Copy link

This is available now in https://pypi.org/project/extra-streamlit-components/, but it is not working for me so a native implementation would be nice

@jrieke
Copy link
Collaborator

jrieke commented Mar 4, 2023

No conrete plan to support this right now but I think at some point we definitely want to have some storage mechanism for the active user. Not sure if this will use cookies, or local storage, or will be tied into Community Cloud...

@ViniciusgCaetano
Copy link

+1!

@abenassi
Copy link

abenassi commented Jul 7, 2023

Hey all just wanted to give a heavy +1000 to this feature request! As far as I have investigated and tested several ST community solutions, there is no way to store a cookie in the client browser that would work in ST Cloud. This is critical for a sane user experience, if not this feels like continuously repeating work, form filling or logins for them…

@janaka
Copy link

janaka commented Sep 8, 2023

💯 this is needed. I'm confused how anybody is running a serious production app, with a great user experience, using Streamlit without this. Please tell us if we are missing something.

@ronald-fenner
Copy link

You can get to the cookies for the app through calls to the internals to get a hold of the tornado's RequstHandler. I was able to do it after looking at the unsuportted method to get the web socket headers and how they did it.
The code to get a hold of the request handler so you can access the cookie methods it has is

import streamlit as st
from streamlit.runtime.scriptrunner import get_script_run_ctx

ctx = get_script_run_ctx()
server = st.runtime.get_instance().get_client(ctx.session_id)
st.write(server.cookies)

Course it would be nice to have a nice st.cookie interface to do it.

@nathan-aa
Copy link

reached here from #680.

In my case, I have a Chatbot, and I wish that:

  1. A chat in the browser tab will not be erased upon tab refreshment - i.e., chat memory cannot be in the session state.
  2. Different tabs on the browser will contain different chats - i.e., chat memory cannot be global or cached.

The required solution is a browser_tab_id or any kind of tab persistent storage.
IMO, this identifier will allow us to manage more advanced features, as mentioned at the beginning of this page.

@Elijas
Copy link

Elijas commented Jan 29, 2024

https://github.com/ktosiek/streamlit-cookies-manager works well

pip install streamlit-cookies-manager

from streamlit_cookies_manager import CookieManager
cookies = CookieManager()

# Get cookies
if not cookies.ready():
    st.stop()
st.write("Current cookies:", cookies)

# Write cookies
cookies["a-cookie"] = value
cookies.save()

@nathan-aa
Copy link

Thank you @Elijas - your cookie manager is fantastic!

It is session-state independent - i.e. does not evaporate upon page refresh (like @ronald-fenner solution)
It works like a charm with a Docker container.

One thing, though, I tried to control the expired_at by setting the _default_expiry to 1 minute - but it didn't seem to work.
any ideas?

this is the code I am using to test the cookies manager:

from datetime import datetime, timedelta
import streamlit as st
from streamlit_cookies_manager import CookieManager

cookie_name = "my_cookie_name"
content = "My cookie content"

class NEW_CM:
    def __init__(self) -> None:
        self.cookie_manager = CookieManager()
        self.cookie_manager._default_expiry = datetime.now() + timedelta(minutes=1)

        if not self.cookie_manager.ready():
            st.stop()

    def set_cookie(self):
        self.cookie_manager[cookie_name] = content
        self.cookie_manager.save()

    def get_cookie(self):
        value = self.cookie_manager.get(cookie_name)
        st.write(f"{cookie_name}: {value}")

    def delete_cookie(self):
        value = None
        if cookie_name in self.cookie_manager:
            value = self.cookie_manager.pop(cookie_name)
        st.write(f"del: {value}")

cookie_manager = NEW_CM()

st.button("Set cookie", on_click=cookie_manager.set_cookie)
st.button("Get Cookie", on_click=cookie_manager.get_cookie)
st.button("Delete cookie", on_click=cookie_manager.delete_cookie)

@Elijas
Copy link

Elijas commented Jan 30, 2024

Thank you @Elijas - your cookie manager is fantastic!

It is session-state independent - i.e. does not evaporate upon page refresh (like @ronald-fenner solution)

It works like a charm with a Docker container.

One thing, though, I tried to control the expired_at by setting the _default_expiry to 1 minute - but it didn't seem to work.

any ideas?

this is the code I am using to test the cookies manager:

from datetime import datetime, timedelta

import streamlit as st

from streamlit_cookies_manager import CookieManager



cookie_name = "my_cookie_name"

content = "My cookie content"



class NEW_CM:

    def __init__(self) -> None:

        self.cookie_manager = CookieManager()

        self.cookie_manager._default_expiry = datetime.now() + timedelta(minutes=1)



        if not self.cookie_manager.ready():

            st.stop()



    def set_cookie(self):

        self.cookie_manager[cookie_name] = content

        self.cookie_manager.save()



    def get_cookie(self):

        value = self.cookie_manager.get(cookie_name)

        st.write(f"{cookie_name}: {value}")



    def delete_cookie(self):

        value = None

        if cookie_name in self.cookie_manager:

            value = self.cookie_manager.pop(cookie_name)

        st.write(f"del: {value}")



cookie_manager = NEW_CM()



st.button("Set cookie", on_click=cookie_manager.set_cookie)

st.button("Get Cookie", on_click=cookie_manager.get_cookie)

st.button("Delete cookie", on_click=cookie_manager.delete_cookie)

Glad I helped!

I'm not the author (or related to the project) so your best bet would be to create a Github Issue straight in that project's issue tab. Thanks!

@karunpoudel-chr
Copy link

karunpoudel-chr commented Feb 2, 2024

The 3rd party components implementing cookies is nice but there are some limitations that could be solved by having it built-in.

Components usually requires extra round trip to get the cookies and would cause python script to rerun (after clients fetches the cookie and returns it using Streamlit.setComponentValue()). Streamlit should be able to expose the cookie available in the current page request in the same run.

Same thing goes for setting and deleting cookies. With build-in implementation, cookies could be set and deleted on client side (sync or async) with confirmation (for sync) without having to rerun the script. With components, you can set the cookie (async only) but if you require response/confirmation from client, it would rerun the script.

This rerun is quite a problem when implementing oauth (authorization code or pkce flow) and you are adding and deleting cookies for login/logout. You have oauth component, cookie getter component, cookie setter component causing multiple reruns and sometime making multiple token request using same authorization code. Sometime a run would halt in the middle by next run (this was weird behavior). I guess, we need build-in library for Oauth 2.0 too.

@PlebeiusGaragicus
Copy link

yes @karunpoudel-chr - this is what I'm running into.

Hacking a cookie manager together is just not working well enough and it's preventing some really cool streamlit use cases.. We need first-party support!

@tol-va
Copy link

tol-va commented Mar 22, 2024

I spent too much time trying to figure something out to do this so I will put my work around here in case it helps someone else. It should work, in theory.

from streamlit.web.server.websocket_headers import _get_websocket_headers
import streamlit.components.v1 as components

userinfo = cookie()
components.html(userinfo.js_output(), width=None, height=None, scrolling=False)

def cookie()
  headers = _get_websocket_headers() // this gets cookies set from client with request
  morsel = cookies.Morsel()
  morsel.domain= <your app domain>
  morsel.path = '/'
  morsel['totalScore'] = if not headers or "totalScore" not in headers ? return 0 : headers['totalScore']
  morsel['score'] = if not headers or "score" not in headers ? return 0 : headers['score']
  return morsel

This makes use of the streamlit.web.server.websocket_headers _get_websocket_headers method that's not really part of the documented features. Why you have a webapp deployment framework without surfacing basic HTTP cookies is beyond me. If you are worried about people using this to steal cookies, implement proper header filtering for your server.

@Elijas
Copy link

Elijas commented Mar 22, 2024

@tol-va

Why you have a webapp deployment framework

I think that's the underlying issue, streamlit is not intended to be a web app framework. It's a data dashboarding framework, i.e. for sliders, charts, and tables, not web app frontends

There was a piece of advice from YCombinator that when users start to consistently "hack" your product into some adjacent use case, it's an important signal to the founders.

And I think there are signs of market traction for the "python frontend developer" use case, some people are even selling $300 templates because they managed to hack streamlit into general-purpose front-end landing pages/websites (with flexible page layouts, OAuth logins etc.).

@heggland
Copy link

bump

@tol-va
Copy link

tol-va commented Apr 1, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature:st.user type:enhancement Requests for feature enhancements or new features
Projects
None yet
Development

No branches or pull requests