User Auth in Local Storage

I recently came across this authentication process, I want to know, how can we avoid Logout when the user reloads the page?

Code snippet:

import streamlit as st
import pandas as pd


# Security
#passlib,hashlib,bcrypt,scrypt
import hashlib
def make_hashes(password):
	return hashlib.sha256(str.encode(password)).hexdigest()

def check_hashes(password,hashed_text):
	if make_hashes(password) == hashed_text:
		return hashed_text
	return False
# DB Management
import sqlite3 
conn = sqlite3.connect('data.db')
c = conn.cursor()
# DB  Functions
def create_usertable():
	c.execute('CREATE TABLE IF NOT EXISTS userstable(username TEXT,password TEXT)')


def add_userdata(username,password):
	c.execute('INSERT INTO userstable(username,password) VALUES (?,?)',(username,password))
	conn.commit()

def login_user(username,password):
	c.execute('SELECT * FROM userstable WHERE username =? AND password = ?',(username,password))
	data = c.fetchall()
	return data


def view_all_users():
	c.execute('SELECT * FROM userstable')
	data = c.fetchall()
	return data



def main():
	"""Simple Login App"""

	st.title("Simple Login App")

	menu = ["Home","Login","SignUp"]
	choice = st.sidebar.selectbox("Menu",menu)

	if choice == "Home":
		st.subheader("Home")

	elif choice == "Login":
		st.subheader("Login Section")

		username = st.sidebar.text_input("User Name")
		password = st.sidebar.text_input("Password",type='password')
		if st.sidebar.checkbox("Login"):
			# if password == '12345':
			create_usertable()
			hashed_pswd = make_hashes(password)

			result = login_user(username,check_hashes(password,hashed_pswd))
			if result:

				st.success("Logged In as {}".format(username))

				task = st.selectbox("Task",["Add Post","Analytics","Profiles"])
				if task == "Add Post":
					st.subheader("Add Your Post")

				elif task == "Analytics":
					st.subheader("Analytics")
				elif task == "Profiles":
					st.subheader("User Profiles")
					user_result = view_all_users()
					clean_db = pd.DataFrame(user_result,columns=["Username","Password"])
					st.dataframe(clean_db)
			else:
				st.warning("Incorrect Username/Password")





	elif choice == "SignUp":
		st.subheader("Create New Account")
		new_user = st.text_input("Username")
		new_password = st.text_input("Password",type='password')

		if st.button("Signup"):
			create_usertable()
			add_userdata(new_user,make_hashes(new_password))
			st.success("You have successfully created a valid Account")
			st.info("Go to Login Menu to login")



if __name__ == '__main__':
	main()

Hi @Aryan_Gupta

Have you tried using cache and session state:

Yes, I tried that.
Could you help me with some code snippets or a straightforward implementation?
I went through that and researched a lot, but could not find how to solve that.

Is there a solution for when the user closes the window and can still stay connected for a certain time?
I tried saving user data in local storage, though access to it requires using javascript (get and set to local storage using st_javascript) which refreshes the page and takes too long.

Cookies are fast but have a limit of 4kb. I am using the component streamlit_local_storage for now. While it works, it took a bit more effort than expected, I think due to reruns of the Streamlit framework but it only added about 1 sec to the load time of the page.

You could maybe write sessionID to a cookie and then use something like AWS DynamoDB to store more.

It seems, that I found a way to overcome the reruns for localStorage usage (and other js code). Feel free to check out this thread

Thanks!
Will look into it.
Can you provide some snippets using your solution?

def ls_get(k, key=None):
    return st_js_blocking(f"return JSON.parse(localStorage.getItem('{k}'));", key)


def ls_set(k, v, key=None):
    jdata = json.dumps(v, ensure_ascii=False)
    st_js_blocking(f"localStorage.setItem('{k}', JSON.stringify({jdata}));", key)


ss = st.session_state
if "user_id" not in ss:
    ss.user_id = ls_get("user_id")

if ss.user_id is None:
    if "new_user_id" not in ss:
        ss.new_user_id = str(uuid.uuid4())
    ls_set("user_id", ss.new_user_id)
    ss.user_id = ss.new_user_id

Notes:

  • Make sure, that st_js_blocking components are accessible from several reruns to actually finish js execution. That’s why, I set ss.user_id = ss.new_user_id after ls_set. Otherwise, on rerun (caused by updated state of component) it will not pass the if condition (ss.user_id is None).
  • I saved new_user_id in ss for the same reason. Otherwise, on rerun it will be a different value, which is not desired behaviour.

So, just remember, that it has to be accessible from rerun or use non blocking versions for more explicit handling.