Cookies support in Streamlit!

This is amazing @MarceTU . Thank you for posting this. Was looking into http cookies, or headers from Streamlit apps for ages! This just made my week! Cheers!! :beers:

in cloud, I can鈥檛 seem to get any of the cookies to work鈥

I鈥檝e put the cookie manager example as a streamlit app and looks like you can鈥檛 set cookies on Cloud?

https://share.streamlit.io/averydata/streamlit-example

1 Like

Well the definition of cloud is broad. But if you specifically mean share.streamlit.io, which its on device cookies are accessible by your application same to other鈥檚, then it鈥檚 a security issue. Which I am not sure if it鈥檚 allowed anymore to do on share.streamlit.io.
However if you host your Streamlit application on a domain/subdomain only accessible by your application then it shall not be an issue and you can easily set cookies using cookie manager.
TLDR; It鈥檚 highly not advised to set user cookies on share.streamlit.io

ah i see. I鈥檒l try again on GCP or Heroku or something鈥hank you

I鈥檓 running into troubles with the CookieManager and getting cookies. I can successfully add cookies to the browser - I can see them in the developer tools. However, when I try getting the cookie, all I get is None. Thoughts, suggestions?

import extra_streamlit_components as stx
import streamlit as st


@st.cache(allow_output_mutation=True, suppress_st_warning=True)
def get_cookie_manager():
    return stx.CookieManager()


cookie_manager = get_cookie_manager()

cookie_name = "Cookie Test"
cookie_value = cookie_manager.get(cookie=cookie_name)
print(f"Cookie value: {cookie_value}")
if cookie_value is None:
    cookie_value = ""

with st.form(key="Cookie"):
    cookie_value = st.text_input(label="Cookie value:", value=cookie_value)

    submitted = st.form_submit_button("Submit")
    if submitted:
        print(f"Submitting: {cookie_value}")
        cookie_manager.set(cookie=cookie_name, val=cookie_value)
        print(f"After set: {cookie_manager.get(cookie=cookie_name)}")

I figured out the problem. I had to change the cookies settings in Chrome to 鈥淎llow all cookies鈥.

However, I still have a problem. When running my streamlit app I can refresh the browser and my cookie will be retrieved. However, if I stop and restart my streamlit app, the cookie isn鈥檛 found despite it showing up in the cookies in the developer tools.

it is not working with on_click

Hey there, thanks for making this. I鈥檓 having some problems with st.error, st.status, or st.success alongside cookies; here is a minimum reproducible example with streamlit 1.10.0

import streamlit as st
import extra_streamlit_components as stx

@st.cache(allow_output_mutation=True)
def get_manager():
    return stx.CookieManager()

cookie_manager = get_manager()

button = st.button("Get cookies")
if button:
    st.subheader("All Cookies:")
    cookies = cookie_manager.get_all()
    st.write(cookies)
    st.success("This should show up for longer than a split second")

The green st.success box shows up for only a split second. Any help is much appreciated!

Edit: After posting this, I realized an example above worked fine using forms. As such, here is an extraordinarily hacky way to solve this bug.

import streamlit as st
import extra_streamlit_components as stx

@st.cache(allow_output_mutation=True)
def get_manager():
    return stx.CookieManager()

cookie_manager = get_manager()

with st.form(key="Cookie"):
    hide_streamlit_style = """
    <style>
    [data-testid="stForm"] {border: none; padding: 0;}
    </style>
    """
    st.markdown(hide_streamlit_style, unsafe_allow_html=True)
    submitted = st.form_submit_button("Get cookies")
    if submitted:
        st.subheader("All Cookies:")
        cookies = cookie_manager.get_all()
        st.write(cookies)
        st.success("This should show up for longer than a split second")

I鈥檝e created a function that gets all cookies from the client, including HTTP-only cookies. The other functions posted by the users get all the cookies from all the sessions connected to the streamlit server. This is bad for security issues if you search for authentication cookies. Here I have a function that gets only the cookies for the client:

import re
from streamlit.server.server import Server
from streamlit.scriptrunner import add_script_run_ctx
def get_cookies() -> dict:
    session_id = add_script_run_ctx().streamlit_script_run_ctx.session_id
    session_info = Server.get_current()._get_session_info(session_id)
    header = session_info.ws.request.headers
    header = dict(header.get_all())
    cookies_str = header["Cookie"]
    results = re.findall(r"([\w]+)=([^;]+)", cookies_str)
    cookies = dict(results)
    return cookies
2 Likes

I cannot make it work,

I see cookies in the browser but none is returned when trying to get them

This streamlit extension is awesome ! :v:t5:

@Mohamed,

when I tried implementing in my app, it caused all sorts of unexpected behavior and was breaking my scripts. Turns out that calling cookie_manager causes the app.py script to run multiple times, and returns the cookies only on the last run. To test, I used session state to track the number of runs and the result:

@st.cache(allow_output_mutation=True)
def get_manager():
     return stx.CookieManager()

cookie_manager = get_manager()
cookies = cookie_manager.get_all()

if 'counter' not in st.session_state:
    st.session_state['counter'] = 0
    st.session_state['result'] = {}
st.session_state['counter'] = st.session_state['counter'] + 1
st.session_state['result'][st.session_state['counter']] = cookies
st.write(st.session_state['result'])

Result:

image

As you can see, the full script ran top to bottom 3 times before finally returning the cookies. That was the first run after starting the server. If I refresh the page, it will only run twice.

If I change 鈥渃ookies = cookie_manager.get_all()鈥 to "cookies = {鈥榤y鈥: 鈥榗ookie鈥檥, the script only runs once as expected:

image

Is this the intended behavior? I鈥檓 guessing this is the reason many are having trouble getting it to work.

1 Like

Same here. Cannot make it work. Cookies are there, but they are not returned. Occasionally it works.

see my post just above. when get_manager() is called, the script is trigger to run multiple times, it doesn鈥檛 return the cookies until the second or third pass. You need to write your script in such a way that it waits until the cookies are returned until proceeding.

I鈥檓 having a similar experience as many other users reporting here. I too experience the 3x loading as described and inconsistent return of cookies despite trying different ways of trying to get around that.

It鈥檚 effectively unusable for me.

Note I have found more luck with streamlit-cookies-manager 路 PyPI
But this library does not work on safari (macOS or iOS) in my testing. There are some issues with it sometimes initially loading as well.

1 Like

Hi,

I wrote a set of functions to get server properties, client headers and client cookies.

They use HACKS discussed in these threads:

Please note: this code is unsupported and will break if the Streamlit dev team change their APIs. You鈥檙e on your own :melting_face:

import re
import streamlit as st
try:
    # Streamlit >= 1.12.0
    from streamlit.web.server.server import Server
    from streamlit.runtime.runtime import Runtime, SessionInfo
    from streamlit.runtime.scriptrunner import add_script_run_ctx
    # from streamlit.runtime.scriptrunner import get_script_run_ctx
except:
    raise Exception('You must use Streamlit >= v1.12.0')

# Mega hack walking the GC heap.
# Look only for singletons that you know exist for the entire streamlit server lifetime, e.g.: Server and Runtime!!
def st_instance_of_type(type_obj: object) -> object:
    import gc
    st_obj = None
    for obj in gc.get_objects():
        if type(obj) is type_obj:
            st_obj = obj
            break
    return st_obj

def st_server_props():
    st_server = st_instance_of_type(Server)

    st_server_runtime = st_server._runtime
    st_gc_runtime = st_instance_of_type(Runtime)
    assert(st_server_runtime == st_gc_runtime)

    main_script_path = st_server.main_script_path
    browser_is_connected = st_server.browser_is_connected

    return {'st_server_runtime': st_server_runtime, 'st_gc_runtime': st_gc_runtime, 'main_script_path': main_script_path, 'browser_is_connected': browser_is_connected}
    
def st_session_info() -> SessionInfo:
    st_runtime = st_instance_of_type(Runtime)
    # get session id from the current script runner thread
    session_id = add_script_run_ctx().streamlit_script_run_ctx.session_id
    # use the session id to retrieve the session info
    session_info = st_runtime._get_session_info(session_id)
    return session_info

def st_client_headers() -> dict:
    session_info = st_session_info()
    client_headers = session_info.client.request.headers._dict
    return dict(client_headers)

def st_client_cookies() -> dict:
    client_headers = st_client_headers()
    cookies_str = client_headers["Cookie"]
    results = re.findall(r"([\w]+)=([^;]+)", cookies_str)
    cookies = dict(results)
    return cookies

st.subheader('Server Props')
st.write(st_server_props())

st.subheader('Client Headers')
st.write(st_client_headers())

st.subheader('Client Cookies')
st.write(st_client_cookies())

HTH,
Arvindra

4 Likes

This is a good method. Unfortunately, it can only query the current session ID. Once I change the cookie, it still returns the previous value. Thank you

lucky! I found an imperfect method, but for no one, it can already be applied in practice.

# Js test
from streamlit_js_eval import get_cookie, set_cookie
from random import randint

# Trigger button
def refresh():  # Refresh Page
    pass
st.button('Refresh', on_click=refresh)

# Trigger mediation rules 锛坕mportant锛
if not 'abc' in st.session_state:  #
    st.session_state['get_cookie_sign'] = 0
    st.session_state['get_cookie1'] = 'cookie1'
    st.session_state['get_cookie2'] = 'cookie11'
    st.session_state['get_cookie3'] = 'cookie111'
    st.session_state['abc'] = 0
elif st.session_state['abc'] == 1:  #
    st.session_state['get_cookie_sign'] = 1
    st.session_state['get_cookie1'] = 'cookie2'
    st.session_state['get_cookie2'] = 'cookie21'
    st.session_state['get_cookie3'] = 'cookie211'
    st.session_state['abc'] = 2
else:  #
    match st.session_state['get_cookie_sign']:
        case 0:
            st.session_state['abc'] += 1
        case 1:
            del st.session_state.abc
    st.write('session_state锛')
    st.write(st.session_state)
    st.session_state['get_cookie_sign'] = 2

# set cookie
print(100)
set_cookie(name='test1', value=randint(0, 100), duration_days=1, component_key='012')
set_cookie(name='test2', value=randint(0, 100), duration_days=1, component_key='013')
set_cookie(name='test3', value=randint(0, 100), duration_days=1, component_key='014')

# get cookie
print(200)
cookie = []
cookie.append(get_cookie(name='test1', component_key=st.session_state['get_cookie1']))
cookie.append(get_cookie(name='test2', component_key=st.session_state['get_cookie2']))
cookie.append(get_cookie(name='test3', component_key=st.session_state['get_cookie3']))

# Printout
st.write('cookie锛')
st.write(cookie)

# Main program interception
if st.session_state['get_cookie_sign'] != 2:
    print('鍋滄锛')
    st.stop()

# Add main program after here
print(300)

Reference source aghasemi/streamlit_js_eval锛氫竴涓嚜瀹氫箟鐨 Streamlit 缁勪欢锛岀敤浜庤瘎浼颁换鎰 Javascript 琛ㄨ揪寮 (github.com)

If you want to delete cookies, please use stx. CookieManager()

1 Like

I have optimized the script logic in practical application, which may improve the running efficiency of the program and reduce unnecessary repeated running, but it may take some time, but I think it will be better than the previous scheme because it reduces the occasional repeated running, and now it looks more stable.

from streamlit_js_eval import get_cookie, set_cookie
from random import randint

# 瑙﹀彂鎸夐挳锛涘湪鏁翠釜绋嬪簭涓繀椤诲瓨鍦ㄦ寜閽瓑琚敤鎴疯Е鍙戠殑缁勪欢锛屽惁鍒欏皢瀵艰嚧绋嬪簭寮傚父寰幆
def shuaxin():  # 鍒锋柊椤甸潰
    # 浠呬粎鏄负浜嗚Е鍙憇t鏇存柊
    pass
st.button('鍒锋柊', on_click=shuaxin)

# set cookie锛涜缃甤ookie
set_cookie(name='test1', value=randint(0, 100), duration_days=1, component_key='012')
set_cookie(name='test2', value=randint(0, 100), duration_days=1, component_key='013')
set_cookie(name='test3', value=randint(0, 100), duration_days=1, component_key='014')

def cookie_obtain():
    # 鑾峰彇cookie
    def cookie_get_cookie():
        import time
        st.session_state['cookies'] = {}
        get_cookie(name='test1', component_key=st.session_state['get_cookie1'])  # 濡傛灉娌℃湁寰楀埌鍝嶅簲淇℃伅锛屾槸None(骞朵笉鏄繑鍥炵殑)
        get_cookie(name='test2', component_key=st.session_state['get_cookie2'])
        get_cookie(name='test4', component_key=st.session_state['get_cookie3'])  # test4 鏄笉瀛樺湪鐨勶紝杩斿洖 ''
        time.sleep(0.06)  # 鍝嶅簲浼氭湁寤惰繜锛屽搷搴斿悗浼氭坊鍔犲埌浼氳瘽锛堟坊鍔犵瓑寰呮椂闂翠箣鍚庯紝涓嶄竴瀹氫細鎵ц閲嶅杩愯锛屼絾鏄伓灏旇繕浼氾級锛涘彲浠ユ寜闇瑕佹潵璋冩暣绛夊緟鏃堕棿锛屼絾鏈濂戒笉瑕佸皬浜0.05绉掞紝0.1绉掓瘮杈冪ǔ瀹

    # 瑙﹀彂璋冭В瑙勫垯锛涢噸瑕佺殑
    if not 'abc' in st.session_state:  # 鈶犻娆℃墦寮椤甸潰銆佺敤鎴稦5鍒锋柊椤甸潰銆佹墽琛屸憽涔嬪悗鐨勭敤鎴疯Е鍙憇t鏇存柊寮濮嬫墽琛屼綅缃
        st.session_state['get_cookie_sign'] = 0
        st.session_state['get_cookie1'] = 'cookie1'
        st.session_state['get_cookie2'] = 'cookie11'
        st.session_state['get_cookie3'] = 'cookie111'
        cookie_get_cookie()  # 鑾峰彇cookie锛屽洜涓鸿幏鍙栦粛鏈夊け璐ョ殑鍙兘锛屽け璐ュ悗浼氱珛鍗抽噸鏂拌繍琛
        st.session_state['abc'] = 0
    elif st.session_state['abc'] == 1:  # 鈶$敤鎴疯Е鍙憇t鏇存柊鎵ц浣嶇疆
        st.session_state['get_cookie_sign'] = 1
        st.session_state['get_cookie1'] = 'cookie2'
        st.session_state['get_cookie2'] = 'cookie21'
        st.session_state['get_cookie3'] = 'cookie211'
        cookie_get_cookie()  # 鑾峰彇cookie锛屽洜涓鸿幏鍙栦粛鏈夊け璐ョ殑鍙兘锛屽け璐ュ悗浼氱珛鍗抽噸鏂拌繍琛
        st.session_state['abc'] = 2
    else:  # 鈶 鈶犵粨鏉熸墽琛屼綅缃
        match st.session_state['get_cookie_sign']:
            case 0:
                st.session_state['abc'] += 1
            case 1:
                del st.session_state.abc
        st.session_state['cookies'] = {
            'test1': st.session_state[st.session_state['get_cookie1']],
            'test2': st.session_state[st.session_state['get_cookie2']],
            'test4': st.session_state[st.session_state['get_cookie3']],
        }
        st.write('session_state锛')
        st.write(st.session_state)
        st.session_state['get_cookie_sign'] = 2

    # 鎵撳嵃杈撳嚭
    st.write('cookie锛')
    st.write(st.session_state['cookies'])

    # 涓荤▼搴忔埅娴
    if st.session_state['get_cookie_sign'] != 2:
        print('鍋滄锛')
        st.stop()
    else:
        return st.session_state['cookies']

cookies = cookie_obtain()
# 鍦ㄦ澶勪箣鍚庢坊鍔犱富绋嬪簭
print('鎵ц涓荤▼搴')

Thanks again 'streamlit_ js_ Eval ', which makes cookie management practical

1 Like