Cookies support in Streamlit!

Hi @Mohamed thanks for your quick reply. Indeed, the webauth_session cookie was set during the login thru Active Directory, not using Streamlit. I’m talking about a deployed service.
It’s also difficult to debug, because in local deployment there is no webauth_login, and all the cookies appear … the problem is to get all the cookies in a deployed service.

If I run the script in this way:

cookies = cookie_manager.get_all()
cookies = cookie_manager.get_all()
st.write(cookies)

I get an error:

streamlit.errors.DuplicateWidgetID: There are multiple identical `st.extra_streamlit_components.CookieManager.cookie_manager` widgets with
`key='get_all'`.

To fix this, please make sure that the `key` argument is unique for
each `st.extra_streamlit_components.CookieManager.cookie_manager` you create.

so I run it in this way:

cookies = cookie_manager.get_all(key='1')
cookies = cookie_manager.get_all(key='2')
st.write(cookies)

But still I can’t see the required cookie containing the web authentication info, in the deployed service.
There is any limitation of this package related to the “HttpOnly” or “Secure” cookie types?

If you set a cookie through the console, such as document.cookie="new_cookie=new_value123" it will show up with cookies = cookie_manager.get_all(). The fact that your cookie was set with AD, make me think it was added with some restrictions, like it got be only accessed from certain domains. Other than that I doubt it’s a problem with the CookieManager.

@Mohamed thanks for your answer. At the end I was able to get the cookie that I was looking for, using this package: from streamlit.server.server import Server

Then I compared the cookies coming from the headers (in the server) with the user cookies from your package to find a match of “ajs_anonymous_id”. In that way I got the email of the user after decoding the cookie. And finally I’m using the email to send back email notifications, but also to provide secure access to pages based on a table of privileges.

:grin:

1 Like

Way to go @MarceTU !
If you don’t mind can you share a code snippet doing this process?

Hi @Mohamed I will add some of the lines to run the code:
The only missing part here is to do a function to decrypt your cookies containing the email from the AUTH login server.

from streamlit.server.server import Server
import extra_streamlit_components as stx
from tornado import httputil #Handle Headers

def get_headers():
    # Hack to get the session object from Streamlit.
    headers=[]
    current_server = Server.get_current()
    if hasattr(current_server, '_session_infos'):
        # Streamlit < 0.56
        session_infos = Server.get_current()._session_infos.values()
    else:
        session_infos = Server.get_current()._session_info_by_id.values()
    # Multiple Session Objects?
    for session_info in session_infos:
        headers.append(session_info.ws.request.headers)
    return headers

def get_email():
  #here code your function to decrypt the cookies and get the email from the JSON body
  return email

def get_email_from_cookies():
    cookie_manager = get_manager()
    cookies = cookie_manager.get_all()
    headers = get_headers()
    for header in headers:
        for (k, v) in sorted(header.get_all()):
            if k == 'Cookie':
                temp_cookie = httputil.parse_cookie(v)
                try:
                    if cookies['ajs_anonymous_id'] in temp_cookie['ajs_anonymous_id']:
                        email = get_email(temp_cookie)
                        st.session_state['email'] = email
                except:
                    continue

#Finally just run:
get_email_from_cookies()

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’t seem to get any of the cookies to work…

I’ve put the cookie manager example as a streamlit app and looks like you can’t set cookies on Cloud?

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

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’s, then it’s a security issue. Which I am not sure if it’s 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’s highly not advised to set user cookies on share.streamlit.io

ah i see. I’ll try again on GCP or Heroku or something…thank you

@MarceTU 's solution was what I was looking for. Thank you so much for sharing your idea!

If your hosting your streamlit app on the domain of an existing site and want to use the site’s cookie-based authentication process, grabbing the cookies from the server object works perfectly.

To share my learnings, here’s a function (based on MarceTU’s) that will pull all the cookies from the server session. Hope it’s helpful for someone!

from streamlit.server.server import Server
from tornado.httputil import parse_cookie

def get_cookies() -> dict:
    session_infos = Server.get_current()._session_info_by_id.values()
    headers = [si.ws.request.headers for si in session_infos]
    cookie_strings = [header_str for header in headers for k, header_str in header.get_all() if k == 'Cookie']
    parsed_cookies = {k: v for c in cookie_strings for k, v in parse_cookie(c).items()}

    return parsed_cookies

I’m 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 “Allow 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’t found despite it showing up in the cookies in the developer tools.

it is not working with on_click