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.

1 Like

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.

1 Like

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.

1 Like

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

2 Likes

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

2 Likes
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.

1 Like