Streamlit-Authenticator icon indicating copy to clipboard operation
Streamlit-Authenticator copied to clipboard

Using the Authenticator with credentials stored in a remote database

Open njt1980 opened this issue 1 year ago • 10 comments

Hi - Can we use the Authenticator when credentials are stored in a remote database?

P.S - Did not find a place to post question, hence posting as an issue. Sorry if that is not per guidelines.

njt1980 avatar Aug 13 '24 11:08 njt1980

Hi @njt1980, thank you for reaching out. It can be done and perhaps this video can help. In the future, I plan on releasing a version that supports hosting the credentials on a remote database. Until then please stay tuned!

mkhorasani avatar Aug 13 '24 11:08 mkhorasani

Thank you for the prompt reponse. I kind of put together some skeleton code for a login page. It does not throw any error, but I see that, the execution seems to be stopping at name, authentication_status, username = authenticator.login({'sidebar'}) and the print statement after that does not seem to be executing. Just wanted to check if I am doing anything wrong. Any direction would be great.

Below is the full code for the page


import streamlit as st
import streamlit_authenticator as stauth
import os
import sqlite3

#Connect to remote database and retrieve user credentials if available
def get_user_credentials(username):
    connection = sqlite3.connect(r"C:\Users\reservation_system.db")
    cursor = connection.cursor()

    #Query to get user details
    cursor.execute("SELECT name,hashed_password FROM credentials WHERE username = ?",(username,))
    user = cursor.fetchone()
    connection.close()
    if user:
        return user[0],user[1]
    return None, None

# Assume the user enters their username and password in a login form
input_username = st.text_input("Username")
input_password = st.text_input("Password", type="password")

if st.button("Login"):
    name, hashed_password = get_user_credentials(input_username)
    print("name :",name)
    print("input_username :", input_username)
    print("hashed_password :",hashed_password)
    
    if name and hashed_password:
        print("Creating authenticator object...")
        # Authenticate the user with the retrieved hashed password
        # authenticator = stauth.Authenticate([name], 
        #                                     [input_username], 
        #                                     [hashed_password], 
        #                                     "user_cookie", 
        #                                     "cookie_key",
        #                                     cookie_expiry_days=1)
        
        authenticator = stauth.Authenticate({"usernames":{
                                                input_username:{
                                                        "name":name,
                                                        "password":hashed_password
                                                    }}
                                            }, 
                                            "user_cookie", 
                                            "cookie_key",
                                           )
        
        # Perform authentication
        print("Authenticating...")
        name, authentication_status, username = authenticator.login({'sidebar'})
        print("Authentication status :",authentication_status)
     
        if st.session_state['authentication_status']:
            authenticator.logout()
            st.write(f'Welcome *{st.session_state["name"]}*')
            st.title('Some content')
        elif st.session_state['authentication_status'] is False:
            st.error('Username/password is incorrect')
        elif st.session_state['authentication_status'] is None:
            st.warning('Please enter your username and password')
        else:
            st.error("Username not found")

njt1980 avatar Aug 13 '24 12:08 njt1980

Incredible package, so appreciated. Associated with this would be the ability to send email/username input and hashed password input to the authenticator. That way I can call remote db to retrieve a single user with one hashed password for the authenticator to match against instead of a big payload of all users

That or allow the authenticator to give us the email input to make a small payload db call before matching passwords?

brdemorin avatar Aug 18 '24 06:08 brdemorin

Incredible package, so appreciated. Associated with this would be the ability to send email/username input and hashed password input to the authenticator. That way I can call remote db to retrieve a single user with one hashed password for the authenticator to match against instead of a big payload of all users

That or allow the authenticator to give us the email input to make a small payload db call before matching passwords?

This is in the pipeline and I hope to release it in Q4 of this year. Please stay tuned!

mkhorasani avatar Aug 18 '24 07:08 mkhorasani

Awesome. And if the ability is there to debundle username/email and password inputs from the authenticator so they can be sent as inputs to a non-render version of it, that allows us to use OAuth for authentication then use your authenticator to handle setting the cookies and session states

brdemorin avatar Aug 18 '24 18:08 brdemorin

Awesome. And if the ability is there to debundle username/email and password inputs from the authenticator so they can be sent as inputs to a non-render version of it, that allows us to use OAuth for authentication then use your authenticator to handle setting the cookies and session states

Yeap, I will consider serving the logic as a service running on a backend server that the user can interface with.

mkhorasani avatar Aug 18 '24 18:08 mkhorasani

I don't know if this is relevant to the discussion, but I use Supabase as a remote DB to hold my authenticator credentials.

I can't quite email a username/password to it, but I can edit the table by hand (which is really just a row holding the JSON) and add a user if I want.

robinzimmermann avatar Oct 16 '24 05:10 robinzimmermann

Thank you very much for this great library! In my case, I am using Google Firestore. I deployed my app to streamlit.app and it is working properly.

felipecordero avatar Oct 19 '24 05:10 felipecordero

@felipecordero Can you maybe make a pull request of your implementation? Or put a link here, so that others can use it as well. Thanks

morphpiece avatar Nov 13 '24 23:11 morphpiece

Hi @morphpiece and thank you for your patience :)

My code is somehow very specific, so I think a pull request could be not fitting everyone approach. But here there is a piece of code from what I implemented:

(Also, feel free to explore the firebase_admin documentation: https://firebase.google.com/docs/admin/setup?hl=en#python)

import firebase_admin
import streamlit_authenticator as stauth
from firebase_admin import credentials, firestore

# Initialize Firebase
if not firebase_admin._apps:
 cred = credentials.Certificate(st.secrets["db_name"].to_dict())
 firebase_admin.initialize_app(cred)

# Starting communication with the firebase db
db = firestore.client()

# Here I read my collection with credentials
cred_ref = db.collection('credentials')
creds = cred_ref.stream()

config = {}

for doc in creds:
 doc_dict = doc.to_dict()
 config[doc.id] = doc_dict

config = config["credentials"]

# Pre-hashing all plain text passwords once
stauth.Hasher.hash_passwords(config['credentials'])

# sample code for saving new credentials

def write_credentials_config_firestore(db: firestore.client, config: dict):
 main_collection = db.collection("credentials")
 main_collection_doc_ref = main_collection.document("credentials")
 main_collection_doc_ref.set(config)

felipecordero avatar Dec 09 '24 15:12 felipecordero