A secure authentication module to manage user access in a Streamlit application
- Quickstart
- Installation
- Creating a config file
- Setup
- Creating a login widget
- Creating a guest login widget 🚀 NEW
- Authenticating users
- Creating a reset password widget
- Creating a new user registration widget
- Creating a forgot password widget
- Creating a forgot username widget
- Creating an update user details widget
- Updating the config file
- License
- Check out the demo app.
- Feel free to visit the API reference.
- And finally follow the tutorial below.
Streamlit-Authenticator is distributed via PyPI:
pip install streamlit-authenticator
Using Streamlit-Authenticator is as simple as importing the module and calling it to verify your user's credentials.
import streamlit as st
import streamlit_authenticator as stauth
- Create a YAML config file and add to it your user's credentials: including username, email, first name, last name, and password (plain text passwords will be hashed automatically).
- Enter a name, random key, and number of days to expiry, for a re-authentication cookie that will be stored on the client's browser to enable password-less re-authentication. If you do not require re-authentication, you may set the number of days to expiry to 0.
- Define an optional list of pre-authorized emails of users who are allowed to register and add their credentials to the config file using the register_user widget.
- Add the optional configuration parameters for OAuth2 if you wish to use the experimental_guest_login button.
- Please remember to update the config file (as shown in step 13) after you use the reset_password, register_user, forgot_password, or update_user_details widgets.
cookie:
expiry_days: 30
key: some_signature_key # Must be a string
name: some_cookie_name
credentials:
usernames:
jsmith:
email: jsmith@gmail.com
failed_login_attempts: 0 # Will be managed automatically
first_name: John
last_name: Smith
logged_in: False # Will be managed automatically
password: abc # Will be hashed automatically
roles: # Optional
- admin
- editor
- viewer
rbriggs:
email: rbriggs@gmail.com
failed_login_attempts: 0 # Will be managed automatically
first_name: Rebecca
last_name: Briggs
logged_in: False # Will be managed automatically
password: def # Will be hashed automatically
roles: # Optional
- viewer
oauth2: # Optional
google: # Follow instructions: https://developers.google.com/identity/protocols/oauth2
client_id: # To be filled
client_secret: # To be filled
redirect_uri: # URL to redirect to after OAuth2 authentication
microsoft: # Follow instructions: https://learn.microsoft.com/en-us/graph/auth-register-app-v2
client_id: # To be filled
client_secret: # To be filled
redirect_uri: # URL to redirect to after OAuth2 authentication
tenant_id: # To be filled
pre-authorized: # Optional
emails:
- melsby@gmail.com
- Please note that the 'failed_login_attempts' and 'logged_in' fields corresponding to each user's number of failed login attempts and log-in status in the credentials will be added and managed automatically.
- Subsequently import the config file into your script and create an authentication object.
import yaml
from yaml.loader import SafeLoader
with open('../config.yaml') as file:
config = yaml.load(file, Loader=SafeLoader)
# Pre-hashing all plain text passwords once
# stauth.Hasher.hash_passwords(config['credentials'])
authenticator = stauth.Authenticate(
config['credentials'],
config['cookie']['name'],
config['cookie']['key'],
config['cookie']['expiry_days']
)
- Plain text passwords will be hashed automatically by default, however, for a large number of users it is recommended to pre-hash the passwords in the credentials using the Hasher.hash_passwords function.
- If you choose to pre-hash the passwords, please set the auto_hash parameter in the Authenticate class to False.
- credentials: dict
- The credentials dict with plain text passwords.
- dict
- The credentials dict with hashed passwords.
- credentials: dict, str
- Dictionary with the usernames, names, passwords, and emails, and other user data, or path pointing to the location of the config file.
- cookie_name: str
- Specifies the name of the re-authentication cookie stored on the client's browser for password-less re-authentication.
- cookie_key: str
- Specifies the key that will be used to hash the signature of the re-authentication cookie.
- cookie_expiry_days: float, default 30.0
- Specifies the number of days before the re-authentication cookie automatically expires on the client's browser.
- validator: Validator, optional, default None
- Provides a validator object that will check the validity of the username, name, and email fields.
- auto_hash: bool, default True
- Automatic hashing requirement for passwords, True: plain text passwords will be hashed automatically, False: plain text passwords will not be hashed automatically.
- **kwargs: dict, optional
- Arguments to pass to the Authenticate class.
- Please remember to pass the authenticator object to each and every page in a multi-page application as a session state variable.
- You can render the login widget as follows.
try:
authenticator.login()
except LoginError as e:
st.error(e)
- location: str, {'main', 'sidebar', 'unrendered'}, default 'main'
- Specifies the location of the login widget.
- max_concurrent_users: int, optional, default None
- Limits the number of concurrent users. If not specified there will be no limit to the number of concurrently logged in users.
- max_login_attempts: int, optional, default None
- Limits the number of failed login attempts. If not specified there will be no limit to the number of failed login attempts.
- fields: dict, optional, default {'Form name':'Login', 'Username':'Username', 'Password':'Password', 'Login':'Login', 'Captcha':'Captcha'}
- Customizes the text of headers, buttons and other fields.
- captcha: bool, default False
- Specifies the captcha requirement for the login widget, True: captcha required, False: captcha removed.
- single_session: bool, default False
- Disables the ability for the same user to log in multiple sessions, True: single session allowed, False: multiple sessions allowed.
- clear_on_submit: bool, default False
- Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.
- key: str, default 'Login'
- Unique key provided to widget to avoid duplicate WidgetID errors.
- callback: callable, optional, default None
- Callback function that will be invoked on form submission with a dict as a parameter.
- Please remember to re-invoke an 'unrendered' login widget on each and every page in a multi-page application.
- You may use the experimental_guest_login button to log in non-registered users with their Google or Microsoft accounts using OAuth2.
- To create the client ID and client secret parameters for Google OAuth2 please refer to Google's documentation.
- To create the client ID, client secret, and tenant ID parameters for Microsoft OAuth2 please refer to Microsoft's documentation.
- Once you have created the OAuth2 configuration parameters, add them to the config file as shown in step 3.
try:
authenticator.experimental_guest_login('Login with Google',
provider='google',
oauth2=config['oauth2'])
authenticator.experimental_guest_login('Login with Microsoft',
provider='microsoft',
oauth2=config['oauth2'])
except LoginError as e:
st.error(e)
- button_name: str, default 'Guest login'
- Rendered name of the guest login button.
- location: str, {'main', 'sidebar'}, default 'main'
- Specifies the location of the guest login button.
- provider: str, {'google', 'microsoft'}, default 'google'
- Selection for OAuth2 provider, Google or Microsoft.
- oauth2: dict, optional, default None
- Configuration parameters to implement an OAuth2 authentication.
- max_concurrent_users: int, optional, default None
- Limits the number of concurrent users. If not specified there will be no limit to the number of concurrently logged in users.
- single_session: bool, default False
- Disables the ability for the same user to log in multiple sessions, True: single session allowed, False: multiple sessions allowed.
- roles: list, optional, default None
- User roles for guest users.
- callback: callable, optional, default None
- Callback function that will be invoked on button press with a dict as a parameter.
- Please note that upon successful login, the guest user's name, email, and other information will be registered in the credentials dictionary and their re-authentication cookie will be saved automatically.
- In addition, you may access the user's roles through st.session_state['roles'].
- You can then retrieve the name, authentication status, and username from Streamlit's session state using st.session_state['name'], st.session_state['authentication_status'], and st.session_state['username'] to allow a verified user to access restricted content.
- You may also render a logout button, or may choose not to render the button if you only need to implement the logout logic programmatically.
- The optional key parameter for the logout button should be used with multi-page applications to prevent Streamlit from throwing duplicate key errors.
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')
- button_name: str, default 'Logout'
- Customizes the button name.
- location: str, {'main', 'sidebar', 'unrendered'}, default 'main'
- Specifies the location of the logout button. If 'unrendered' is passed, the logout logic will be executed without rendering the button.
- key: str, default None
- Unique key that should be used in multi-page applications.
- callback: callable, optional, default None
- Callback function that will be invoked on form submission with a dict as a parameter.
- Or prompt an unverified user to enter a correct username and password.
- You may also retrieve the number of failed login attempts a user has made by accessing st.session_state['failed_login_attempts'] which returns a dictionary with the username as key and the number of failed attempts as the value.
- You may use the reset_password widget to allow a logged in user to modify their password as shown below.
if st.session_state['authentication_status']:
try:
if authenticator.reset_password(st.session_state['username']):
st.success('Password modified successfully')
except Exception as e:
st.error(e)
- username: str
- Specifies the username of the user to reset the password for.
- location: str, {'main', 'sidebar'}, default 'main'
- Specifies the location of the reset password widget.
- fields: dict, optional, default {'Form name':'Reset password', 'Current password':'Current password', 'New password':'New password', 'Repeat password': 'Repeat password', 'Reset':'Reset'}
- Customizes the text of headers, buttons and other fields.
- clear_on_submit: bool, default False
- Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.
- key: str, default 'Reset password'
- Unique key provided to widget to avoid duplicate WidgetID errors.
- callback: callable, optional, default None
- Callback function that will be invoked on form submission with a dict as a parameter.
- bool
- Status of resetting the password.
- Please remember to update the config file (as shown in step 13) after you use this widget.
- You may use the register_user widget to allow a user to sign up to your application as shown below.
- If you require the user to be pre-authorized, define a pre_authorized list of emails that are allowed to register, and add it to the config file or provide it as a parameter to the register_user widget.
- Once they have registered, their email will be automatically removed from the pre_authorized list.
- Alternatively, to allow anyone to sign up, do not provide a pre_authorized list.
try:
email_of_registered_user, \
username_of_registered_user, \
name_of_registered_user = authenticator.register_user(pre_authorized=config['pre-authorized'])
if email_of_registered_user:
st.success('User registered successfully')
except Exception as e:
st.error(e)
- location: str, {'main', 'sidebar'}, default 'main'
- Specifies the location of the register user widget.
- pre_authorized: list, optional, default None
- List of emails of unregistered users who are authorized to register. If no list is provided, all users will be allowed to register.
- domains: list, optional, default None
- Specifies the required list of domains a new email must belong to i.e. ['gmail.com', 'yahoo.com'], list: the required list of domains, None: any domain is allowed.
- fields: dict, optional, default {'Form name':'Register user', 'Email':'Email', 'Username':'Username', 'Password':'Password', 'Repeat password':'Repeat password', 'Password hint':'Password hint', 'Captcha':'Captcha', 'Register':'Register'}
- Customizes the text of headers, buttons and other fields.
- captcha: bool, default True
- Specifies the captcha requirement for the register user widget, True: captcha required, False: captcha removed.
- roles: list, optional, default None
- User roles for registered users.
- merge_username_email: bool, default False
- Merges username into email field, True: username will be the same as the email, False: username and email will be independent.
- clear_on_submit: bool, default False
- Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.
- key: str, default 'Register user'
- Unique key provided to widget to avoid duplicate WidgetID errors.
- callback: callable, optional, default None
- Callback function that will be invoked on form submission with a dict as a parameter.
- str
- Email associated with the new user.
- str
- Username associated with the new user.
- str
- Name associated with the new user.
- Please note that you may access the user's roles through st.session_state['roles'].
- Please remember to update the config file (as shown in step 13) after you use this widget.
- You may use the forgot_password widget to allow a user to generate a new random password.
- The new password will be automatically hashed and saved in the credentials dictionary.
- The widget will return the username, email, and new random password which the developer should then transfer to the user securely.
try:
username_of_forgotten_password, \
email_of_forgotten_password, \
new_random_password = authenticator.forgot_password()
if username_of_forgotten_password:
st.success('New password to be sent securely')
# The developer should securely transfer the new password to the user.
elif username_of_forgotten_password == False:
st.error('Username not found')
except Exception as e:
st.error(e)
- location: str, {'main', 'sidebar'}, default 'main'
- Specifies the location of the forgot password widget.
- fields: dict, optional, default {'Form name':'Forgot password', 'Username':'Username', 'Captcha':'Captcha', 'Submit':'Submit'}
- Customizes the text of headers, buttons and other fields.
- captcha: bool, default False
- Specifies the captcha requirement for the forgot password widget, True: captcha required, False: captcha removed.
- clear_on_submit: bool, default False
- Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.
- key: str, default 'Forgot password'
- Unique key provided to widget to avoid duplicate WidgetID errors.
- callback: callable, optional, default None
- Callback function that will be invoked on form submission with a dict as a parameter.
- str
- Username associated with the forgotten password.
- str
- Email associated with the forgotten password.
- str
- New plain text password that should be transferred to the user securely.
- Please remember to update the config file (as shown in step 13) after you use this widget.
- You may use the forgot_username widget to allow a user to retrieve their forgotten username.
- The widget will return the username and email which the developer should then transfer to the user securely.
try:
username_of_forgotten_username, \
email_of_forgotten_username = authenticator.forgot_username()
if username_of_forgotten_username:
st.success('Username to be sent securely')
# The developer should securely transfer the username to the user.
elif username_of_forgotten_username == False:
st.error('Email not found')
except Exception as e:
st.error(e)
- location: str, {'main', 'sidebar'}, default 'main'
- Specifies the location of the forgot username widget.
- fields: dict, optional, default {'Form name':'Forgot username', 'Email':'Email', 'Captcha':'Captcha', 'Submit':'Submit'}
- Customizes the text of headers, buttons and other fields.
- captcha: bool, default False
- Specifies the captcha requirement for the forgot username widget, True: captcha required, False: captcha removed.
- clear_on_submit: bool, default False
- Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.
- key: str, default 'Forgot username'
- Unique key provided to widget to avoid duplicate WidgetID errors.
- callback: callable, optional, default None
- Callback function that will be invoked on form submission with a dict as a parameter.
- str
- Forgotten username that should be transferred to the user securely.
- str
- Email associated with the forgotten username.
- You may use the update_user_details widget to allow a logged in user to update their name and/or email.
- The widget will automatically save the updated details in both the credentials dictionary and re-authentication cookie.
if st.session_state['authentication_status']:
try:
if authenticator.update_user_details(st.session_state['username']):
st.success('Entries updated successfully')
except Exception as e:
st.error(e)
- username: str
- Specifies the username of the user to update user details for.
- location: str, {'main', 'sidebar'}, default 'main'
- Specifies the location of the update user details widget.
- fields: dict, optional, default {'Form name':'Update user details', 'Field':'Field', 'First name':'First name', 'Last name':'Last name', 'Email':'Email', 'New value':'New value', 'Update':'Update'}
- Customizes the text of headers, buttons and other fields.
- clear_on_submit: bool, default False
- Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.
- key: str, default 'Update user details'
- Unique key provided to widget to avoid duplicate WidgetID errors.
- callback: callable, optional, default None
- Callback function that will be invoked on form submission with a dict as a parameter.
- bool
- Status of updating the user details.
- Please remember to update the config file (as shown in step 13) after you use this widget.
- Please ensure that the config file is re-saved anytime the credentials are updated or whenever the reset_password, register_user, forgot_password, or update_user_details widgets are used.
with open('../config.yaml', 'w') as file:
yaml.dump(config, file, default_flow_style=False)
This project is proprietary software. The use of this software is governed by the terms specified in the LICENSE file. Unauthorized copying, modification, or distribution of this software is prohibited.