Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using the Authenticator with credentials stored in a remote database #193

Open
njt1980 opened this issue Aug 13, 2024 · 10 comments
Open

Using the Authenticator with credentials stored in a remote database #193

njt1980 opened this issue Aug 13, 2024 · 10 comments
Labels
help wanted Extra attention is needed

Comments

@njt1980
Copy link

njt1980 commented Aug 13, 2024

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.

@mkhorasani mkhorasani added the help wanted Extra attention is needed label Aug 13, 2024
@mkhorasani
Copy link
Owner

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!

@njt1980
Copy link
Author

njt1980 commented Aug 13, 2024

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")

@brdemorin
Copy link

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?

@mkhorasani
Copy link
Owner

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!

@brdemorin
Copy link

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

@mkhorasani
Copy link
Owner

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.

@robinzimmermann
Copy link

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.

@felipecordero
Copy link

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.

@morphpiece
Copy link

@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

@felipecordero
Copy link

felipecordero commented Dec 9, 2024

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

6 participants