Search code examples
pythonvisual-studio-codestreamlit

streamlit Authenticate.__init__() got multiple values for argument 'cookie_expiry_days'


I'm building a basic streamlit app. In order to validate user credentials, I'm using the authentification module of streamlit. Only one problem, the code does not work !

ERROR

TypeError: Authenticate.init() got multiple values for argument 'cookie_expiry_days'

Traceback:

File "C:\Users_M92\Desktop\Coding\Python\Projects\Personal1\venv\lib\site-packages\streamlit\scriptrunner\script_runner.py", line 557, in _run_script exec(code, module.dict)

File "app.py", line 23, in authenticator = stauth.Authenticate(names, usernames, hashed_passwords,

By the way, I used the same code described here in the official documentation :

CODE

import streamlit as st
import streamlit_authenticator as stauth

names = ['John Smith', 'Rebecca Briggs']
usernames = ['jsmith', 'rbriggs']
passwords = ['123', '456']

hashed_passwords = stauth.Hasher(passwords).generate()

authenticator = stauth.Authenticate(names, usernames, hashed_passwords,
    'some_cookie_name', 'some_signature_key', cookie_expiry_days=30)

name, authentication_status, username = authenticator.login('Login', 'main')

if authentication_status:
    authenticator.logout('Logout', 'main')
    st.write('Welcome *%s*' % (name))
    st.title('Some content')
elif authentication_status == False:
    st.error('Username/password is incorrect')
elif authentication_status == None:
    st.warning('Please enter your username and password')

if st.session_state['authentication_status']:
    st.write('Welcome *%s*' % (st.session_state['name']))
    st.title('Some content')
elif st.session_state['authentication_status'] == False:
    st.error('Username/password is incorrect')
elif st.session_state['authentication_status'] == None:
    st.warning('Please enter your username and password')

When I removed the argument cookie_expiry_days=30, I get this error instead :

TypeError: list indices must be integers or slices, not str

File"C:\Users_M92\Desktop\Coding\Python\Projects\Personal1\venv\lib\site-packages\streamlit_authenticator\authenticate.py",line 36, in __init__self.credentials['usernames'] = {key.lower(): value for key, value in credentials['usernames'].items()}

Do you have any idea why I keep getting these errors ?


Solution

  • So since 2 days later and still no reply from OP :p here's what I found after checking out 0.2.1 as ferdy pointed out...

    the new format requires a credentials dictionary, the example given is like this

    credentials:   
     usernames:
        jsmith:
          email: jsmith@gmail.com
          name: John Smith
          password: '123' # To be replaced with hashed password
        rbriggs:
          email: rbriggs@gmail.com
          name: Rebecca Briggs
          password: '456' # To be replaced with hashed password 
     cookie:   expiry_days: 30   
     key: some_signature_key   
     name: some_cookie_name 
     preauthorized:   emails:
       - melsby@gmail.com
    

    but since that wants a yaml and I basically had it all set up the old way (based on Coding is Fun's youtube tutorial), i wanted to just pass the credentials to the authenticator directly, so if your original code is like this...

    authenticator = stauth.Authenticate(names, usernames, passwords, "app_home", "auth", cookie_expiry_days=30)
    

    what you want is a dictionary like this

        credentials = {
            "usernames":{
                usernames[0]:{
                    "name":names[0],
                    "password":passwords[0]
                    },
                usernames[1]:{
                    "name":names[1],
                    "password":passwords[1]
                    }            
                }
            }
    

    Then you pass that to authenticator like so...

    authenticator = stauth.Authenticate(credentials, "app_home", "auth", cookie_expiry_days=30)
    

    so just to clarify your resulting credentials dictionary will look something like this...

        credentials = {
            "usernames":{
                "jsmith92":{
                    "name":"john smith",
                    "password":"$2b$12$TSuKwWML0EpbohBQgHx4p8E5q"
                    },
                "tturner":{
                    "name":"timmy turner",
                    "password":"$2b$12$asdaUduuibuEIyBUBHASD896a"
                    }            
                }
            }
    

    As you may note I've not included the cookie part in the dictionary (as they state you should in the config.yaml) and it still works just fine

    Now of course you can fill in these variables in a loop based on how many items you have in each list (names, passwords, usernames) but if you just want to quickly test one or two sets of credentials this is a quick and easy way to do so.

    For reference here is the loop that i used to create the final credentials dictionary dynamically based on any number of credentials in the users, names, and passwords lists...

    credentials = {"usernames":{}}
    
    for un, name, pw in zip(usernames, names, passwords):
        user_dict = {"name":name,"password":pw}
        credentials["usernames"].update({un:user_dict})
    
    authenticator = stauth.Authenticate(credentials, "app_home", "auth", cookie_expiry_days=30)
    

    First initialise the credentials dictionary with the usernames key and an empty dictionary as the value.

    Then zip the credentials (usernames, names, password) together and loop them. Note for my example - un means username, pw means password

    Then we add each user_dict to the credentials dictionary as a value under the "usernames" key by using the update function. Note - be sure to wrap the dictionary in {} in the update part i.e. .update({un:user_dict})

    You may also want to add a logout button once the user has verified since the login form disappears once the user is authenticated (30 day cookies) making it practically impossible to test if you don't have one.

    You can do that with a simple if statement, using the authentication_status from earlier like so...

    if authentication_status == True:
        authenticator.logout("logout","main")
    

    Sorry its long but figured id update to include the actual loop i used, here is the code I used which is working, in it's entirety for anyone who wants it

        # my class function which makes a call to a database and returns a list of lists (nested list), of usernames, names, and passwords
        users = usr.get_all_users_credentials()
        # the code mentioned above
        usernames = [user[1] for user in users]
        names = [user[2] for user in users]
        passwords = [user[3] for user in users]
    
        credentials = {"usernames":{}}
    
        for un, name, pw in zip(usernames, names, passwords):
            user_dict = {"name":name,"password":pw}
            credentials["usernames"].update({un:user_dict})
    
        authenticator = stauth.Authenticate(credentials, "app_home", "auth", cookie_expiry_days=30)
    
        name, authentication_status, username = authenticator.login("Login", "main")
    
        if authentication_status == True:
            authenticator.logout("logout","main")