<img src="https://raw.githubusercontent.com/mkhorasani/Streamlit-Authenticator/main/graphics/logo.png" alt="Streamlit Authenticator logo" style="margin-top:50px;width:450px"></img>
<!--- [![Downloads](https://pepy.tech/badge/streamlit-authenticator)](https://pepy.tech/project/streamlit-authenticator) --->
<!--- [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/khorasani) --->
**A secure authentication module to manage user access in a Streamlit application**
[![Downloads](https://static.pepy.tech/badge/streamlit-authenticator)](https://pepy.tech/project/streamlit-authenticator)
[![Downloads](https://static.pepy.tech/badge/streamlit-authenticator/month)](https://pepy.tech/project/streamlit-authenticator)
[![Downloads](https://static.pepy.tech/badge/streamlit-authenticator/week)](https://pepy.tech/project/streamlit-authenticator)
<!--- <br/><br/><br/> ---?
<!--- <a href="http://tinyurl.com/2p8mw32d"><img src="https://raw.githubusercontent.com/mkhorasani/streamlit_authenticator_test/main/Web%20App%20Web%20Dev%20with%20Streamlit%20-%20Cover.png" width="300" height="450"> --->
<!--- ###### _To learn more please refer to my book [Web Application Development with Streamlit](http://tinyurl.com/2p8mw32d)._ --->
## Table of Contents
- [Quickstart](#1-quickstart)
- [Installation](#2-installation)
- [Creating a configuration file](#3-creating-a-configuration-file)
- [Setup](#4-setup)
- [Creating a login widget](#5-creating-a-login-widget)
- [Creating a guest login widget](#6-creating-a-guest-login-widget)
- [Authenticating users](#7-authenticating-users)
- [Creating a reset password widget](#8-creating-a-reset-password-widget)
- [Creating a new user registration widget](#9-creating-a-new-user-registration-widget)
- [Creating a forgot password widget](#10-creating-a-forgot-password-widget)
- [Creating a forgot username widget](#11-creating-a-forgot-username-widget)
- [Creating an update user details widget](#12-creating-an-update-user-details-widget)
- [Updating the configuration file](#13-updating-the-configuration-file)
- [License](#license)
### 1. Quickstart
* Check out the [demo app](https://demo-app-v0-3-3.streamlit.app/).
* Please refer to the [API reference](https://streamlit-authenticator.readthedocs.io/en/latest/).
### 2. Installation
Streamlit-Authenticator is distributed via [PyPI](https://pypi.org/project/streamlit-authenticator/):
```python
pip install streamlit-authenticator
```
Using Streamlit-Authenticator is as simple as importing the module and calling it to verify your user's credentials.
```python
import streamlit as st
import streamlit_authenticator as stauth
```
### 3. Creating a configuration file
* Initially create a YAML configuration file and add to it your users' credentials: including names, usernames, and passwords (plain text passwords will be hashed automatically).
* In addition, 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.
* Finally, define a list of pre-authorized emails of users who can register and add their credentials to the configuration file with the use of the **register_user** widget.
* **_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._**
```python
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
logged_in: False # Will be managed automatically
name: John Smith
password: abc # Will be hashed automatically
roles:
- admin
- editor
- viewer
rbriggs:
email: rbriggs@gmail.com
failed_login_attempts: 0 # Will be managed automatically
logged_in: False # Will be managed automatically
name: Rebecca Briggs
password: def # Will be hashed automatically
roles:
- viewer
oauth2:
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:
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._
### 4. Setup
* Subsequently import the configuration file into your script and create an authentication object.
```python
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.
> ### Hasher.hash_passwords
> #### Parameters:
> - **credentials:** _dict_
> - The credentials dict with plain text passwords.
> #### Returns:
> - _dict_
> - The credentials dict with hashed passwords.
> ### Authenticate
> #### Parameters:
> - **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._**
### 5. Creating a login widget
* You can render the login widget as follows.
```python
authenticator.login()
```
> ### Authenticate.login
> #### Parameters:
> - **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.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/login_form.JPG)
* **_Please remember to re-invoke an 'unrendered' login widget on each and every page in a multi-page application._**
### 6. Creating a guest login widget
* You can render the guest login widget as follows.
```python
authenticator.experimental_guest_login('Login with Google', provider='google', oauth2=config['oauth2'])
authenticator.experimental_guest_login('Login with Microsoft', provider='microsoft', oauth2=config['oauth2'])
```
> ### Authenticate.experimental_guest_login
> #### Parameters:
> - **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.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/guest_login_buttons.JPG)
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/guest_login_google.JPG)
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/guest_login_microsoft.JPG)
* You may implement the guest login widget using both Google and Microsoft OAuth2 handles.
* To create the client id and client secret parameters for Google OAuth2 please refer to https://developers.google.com/identity/protocols/oauth2.
* To create the client id, client secret, and tenant id parameters for Microsoft OAuth2 please refer to https://learn.microsoft.com/en-us/graph/auth-register-app-v2.
* 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.
### 7. Authenticating user
* 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.
```python
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')
```
> ### Authenticate.logout
> #### Parameters:
> - **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.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/logged_in.JPG)
* Or prompt an unverified user to enter a correct username and password.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/incorrect_login.JPG)
* 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.
### 8. Creating a reset password widget
* You may use the **reset_password** widget to allow a logged in user to modify their password as shown below.
```python
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)
```
> ### Authenticate.reset_password
> #### Parameters:
> - **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.
> #### Returns::
> - _bool_
> - Status of resetting the password.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/reset_password.JPG)
* **_Please remember to update the config file (as shown in step 13) after you use this widget._**
### 9. Creating a new user registration 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 configuration 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.
```python
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)
```
> ### Authenticate.register_user
> #### Parameters:
> - **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.
> #### Returns:
> - _str_
> - Email associated with the new user.
> - _str_
> - Username associated with the new user.
> - _str_
> - Name associated with the new user.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/register_user.JPG)
* **_Please remember to update the config file (as shown in step 13) after you use this widget._**
### 10. Creating a forgot password 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.
```python
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)
```
> ### Authenticate.forgot_password
> #### Parameters
> - **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.
> #### Returns:
> - _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.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/forgot_password.JPG)
* **_Please remember to update the config file (as shown in step 13) after you use this widget._**
### 11. Creating a forgot username 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.
```python
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)
```
> ### Authenticate.forgot_username
> #### Parameters
> - **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.
> #### Returns:
> - _str_
> - Forgotten username that should be transferred to the user securely.
> - _str_
> - Email associated with the forgotten username.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/forgot_username.JPG)
### 12. Creating an update user details widget
* 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.
```python
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)
```
> ### Authenticate.update_user_details
> #### Parameters
> - **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.
> #### Returns:
> - _bool_
> - Status of updating the user details.
![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/update_user_details.JPG)
* **_Please remember to update the config file (as shown in step 13) after you use this widget._**
### 13. Updating the configuration file
* Please ensure that the configuration 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.
```python
with open('../config.yaml', 'w') as file:
yaml.dump(config, file, default_flow_style=False)
```
## License
This project is proprietary software. The use of this software is governed by the terms specified in the [LICENSE](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/LICENSE) file. Unauthorized copying, modification, or distribution of this software is prohibited.
Raw data
{
"_id": null,
"home_page": "https://github.com/mkhorasani/Streamlit-Authenticator",
"name": "streamlit-authenticator",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": null,
"keywords": "Python, Streamlit, Authentication, Components",
"author": "Mohammad Khorasani",
"author_email": "khorasani.mohammad@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/f8/70/04c24fb2d9b372edfa2261cbec3828633bc997c4bf29310ff53cf092a647/streamlit_authenticator-0.4.1.tar.gz",
"platform": null,
"description": "<img src=\"https://raw.githubusercontent.com/mkhorasani/Streamlit-Authenticator/main/graphics/logo.png\" alt=\"Streamlit Authenticator logo\" style=\"margin-top:50px;width:450px\"></img>\r\n<!--- [![Downloads](https://pepy.tech/badge/streamlit-authenticator)](https://pepy.tech/project/streamlit-authenticator) --->\r\n<!--- [![\"Buy Me A Coffee\"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/khorasani) --->\r\n\r\n**A secure authentication module to manage user access in a Streamlit application**\r\n\r\n[![Downloads](https://static.pepy.tech/badge/streamlit-authenticator)](https://pepy.tech/project/streamlit-authenticator)\r\n[![Downloads](https://static.pepy.tech/badge/streamlit-authenticator/month)](https://pepy.tech/project/streamlit-authenticator)\r\n[![Downloads](https://static.pepy.tech/badge/streamlit-authenticator/week)](https://pepy.tech/project/streamlit-authenticator)\r\n<!--- <br/><br/><br/> ---?\r\n<!--- <a href=\"http://tinyurl.com/2p8mw32d\"><img src=\"https://raw.githubusercontent.com/mkhorasani/streamlit_authenticator_test/main/Web%20App%20Web%20Dev%20with%20Streamlit%20-%20Cover.png\" width=\"300\" height=\"450\"> --->\r\n\r\n<!--- ###### _To learn more please refer to my book [Web Application Development with Streamlit](http://tinyurl.com/2p8mw32d)._ --->\r\n\r\n## Table of Contents\r\n- [Quickstart](#1-quickstart)\r\n- [Installation](#2-installation)\r\n- [Creating a configuration file](#3-creating-a-configuration-file)\r\n- [Setup](#4-setup)\r\n- [Creating a login widget](#5-creating-a-login-widget)\r\n- [Creating a guest login widget](#6-creating-a-guest-login-widget)\r\n- [Authenticating users](#7-authenticating-users)\r\n- [Creating a reset password widget](#8-creating-a-reset-password-widget)\r\n- [Creating a new user registration widget](#9-creating-a-new-user-registration-widget)\r\n- [Creating a forgot password widget](#10-creating-a-forgot-password-widget)\r\n- [Creating a forgot username widget](#11-creating-a-forgot-username-widget)\r\n- [Creating an update user details widget](#12-creating-an-update-user-details-widget)\r\n- [Updating the configuration file](#13-updating-the-configuration-file)\r\n- [License](#license)\r\n\r\n### 1. Quickstart\r\n\r\n* Check out the [demo app](https://demo-app-v0-3-3.streamlit.app/).\r\n* Please refer to the [API reference](https://streamlit-authenticator.readthedocs.io/en/latest/).\r\n\r\n### 2. Installation\r\n\r\nStreamlit-Authenticator is distributed via [PyPI](https://pypi.org/project/streamlit-authenticator/):\r\n\r\n```python\r\npip install streamlit-authenticator\r\n```\r\n\r\nUsing Streamlit-Authenticator is as simple as importing the module and calling it to verify your user's credentials.\r\n\r\n```python\r\nimport streamlit as st\r\nimport streamlit_authenticator as stauth\r\n```\r\n\r\n### 3. Creating a configuration file\r\n\r\n* Initially create a YAML configuration file and add to it your users' credentials: including names, usernames, and passwords (plain text passwords will be hashed automatically).\r\n* In addition, 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.\r\n* Finally, define a list of pre-authorized emails of users who can register and add their credentials to the configuration file with the use of the **register_user** widget.\r\n* **_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._**\r\n\r\n```python\r\ncookie:\r\n expiry_days: 30\r\n key: some_signature_key # Must be a string\r\n name: some_cookie_name\r\ncredentials:\r\n usernames:\r\n jsmith:\r\n email: jsmith@gmail.com\r\n failed_login_attempts: 0 # Will be managed automatically\r\n logged_in: False # Will be managed automatically\r\n name: John Smith\r\n password: abc # Will be hashed automatically\r\n roles:\r\n - admin\r\n - editor\r\n - viewer\r\n rbriggs:\r\n email: rbriggs@gmail.com\r\n failed_login_attempts: 0 # Will be managed automatically\r\n logged_in: False # Will be managed automatically\r\n name: Rebecca Briggs\r\n password: def # Will be hashed automatically\r\n roles:\r\n - viewer\r\noauth2:\r\n google: # Follow instructions: https://developers.google.com/identity/protocols/oauth2\r\n client_id: # To be filled\r\n client_secret: # To be filled\r\n redirect_uri: # URL to redirect to after OAuth2 authentication\r\n microsoft: # Follow instructions: https://learn.microsoft.com/en-us/graph/auth-register-app-v2\r\n client_id: # To be filled\r\n client_secret: # To be filled\r\n redirect_uri: # URL to redirect to after OAuth2 authentication\r\n tenant_id: # To be filled\r\npre-authorized:\r\n emails:\r\n - melsby@gmail.com\r\n```\r\n\r\n* _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._\r\n\r\n### 4. Setup\r\n\r\n* Subsequently import the configuration file into your script and create an authentication object.\r\n\r\n```python\r\nimport yaml\r\nfrom yaml.loader import SafeLoader\r\n\r\nwith open('../config.yaml') as file:\r\n config = yaml.load(file, Loader=SafeLoader)\r\n\r\n# Pre-hashing all plain text passwords once\r\n# stauth.Hasher.hash_passwords(config['credentials'])\r\n\r\nauthenticator = stauth.Authenticate(\r\n config['credentials'],\r\n config['cookie']['name'],\r\n config['cookie']['key'],\r\n config['cookie']['expiry_days']\r\n)\r\n```\r\n\r\n* 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.\r\n* If you choose to pre-hash the passwords, please set the **auto_hash** parameter in the **Authenticate** class to False.\r\n\r\n> ### Hasher.hash_passwords\r\n> #### Parameters:\r\n> - **credentials:** _dict_\r\n> - The credentials dict with plain text passwords.\r\n> #### Returns:\r\n> - _dict_\r\n> - The credentials dict with hashed passwords.\r\n\r\n> ### Authenticate\r\n> #### Parameters:\r\n> - **credentials:** _dict, str_\r\n> - Dictionary with the usernames, names, passwords, and emails, and other user data, or path pointing to the location of the config file.\r\n> - **cookie_name:** _str_\r\n> - Specifies the name of the re-authentication cookie stored on the client's browser for password-less re-authentication.\r\n> - **cookie_key:** _str_\r\n> - Specifies the key that will be used to hash the signature of the re-authentication cookie.\r\n> - **cookie_expiry_days:** _float, default 30.0_\r\n> - Specifies the number of days before the re-authentication cookie automatically expires on the client's browser.\r\n> - **validator:** _Validator, optional, default None_\r\n> - Provides a validator object that will check the validity of the username, name, and email fields.\r\n> - **auto_hash:** _bool, default True_\r\n> - Automatic hashing requirement for passwords, True: plain text passwords will be hashed automatically, False: plain text passwords will not be hashed automatically.\r\n> - ****kwargs:** _dict, optional_\r\n> - Arguments to pass to the Authenticate class.\r\n\r\n* **_Please remember to pass the authenticator object to each and every page in a multi-page application as a session state variable._**\r\n\r\n### 5. Creating a login widget\r\n\r\n* You can render the login widget as follows.\r\n\r\n```python\r\nauthenticator.login()\r\n```\r\n\r\n> ### Authenticate.login\r\n> #### Parameters:\r\n> - **location:** _str, {'main', 'sidebar', 'unrendered'}, default 'main'_\r\n> - Specifies the location of the login widget.\r\n> - **max_concurrent_users:** _int, optional, default None_\r\n> - Limits the number of concurrent users. If not specified there will be no limit to the number of concurrently logged in users.\r\n> - **max_login_attempts:** _int, optional, default None_\r\n> - Limits the number of failed login attempts. If not specified there will be no limit to the number of failed login attempts.\r\n> - **fields:** _dict, optional, default {'Form name':'Login', 'Username':'Username', 'Password':'Password', 'Login':'Login', 'Captcha':'Captcha'}_\r\n> - Customizes the text of headers, buttons and other fields.\r\n> - **captcha:** _bool, default False_\r\n> - Specifies the captcha requirement for the login widget, True: captcha required, False: captcha removed.\r\n> - **single_session:** _bool, default False_\r\n> - Disables the ability for the same user to log in multiple sessions, True: single session allowed, False: multiple sessions allowed.\r\n> - **clear_on_submit:** _bool, default False_\r\n> - Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.\r\n> - **key:** _str, default 'Login'_\r\n> - Unique key provided to widget to avoid duplicate WidgetID errors.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on form submission with a dict as a parameter.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/login_form.JPG)\r\n\r\n* **_Please remember to re-invoke an 'unrendered' login widget on each and every page in a multi-page application._**\r\n\r\n### 6. Creating a guest login widget\r\n\r\n* You can render the guest login widget as follows.\r\n\r\n```python\r\nauthenticator.experimental_guest_login('Login with Google', provider='google', oauth2=config['oauth2'])\r\nauthenticator.experimental_guest_login('Login with Microsoft', provider='microsoft', oauth2=config['oauth2'])\r\n```\r\n\r\n> ### Authenticate.experimental_guest_login\r\n> #### Parameters:\r\n> - **button_name:** _str, default 'Guest login'_\r\n> - Rendered name of the guest login button.\r\n> - **location:** _str, {'main', 'sidebar'}, default 'main'_\r\n> - Specifies the location of the guest login button.\r\n> - **provider:** _str, {'google', 'microsoft'}, default 'google'_\r\n> - Selection for OAuth2 provider, Google or Microsoft.\r\n> - **oauth2:** _dict, optional, default None_\r\n> - Configuration parameters to implement an OAuth2 authentication.\r\n> - **max_concurrent_users:** _int, optional, default None_\r\n> - Limits the number of concurrent users. If not specified there will be no limit to the number of concurrently logged in users.\r\n> - **single_session:** _bool, default False_\r\n> - Disables the ability for the same user to log in multiple sessions, True: single session allowed, False: multiple sessions allowed.\r\n> - **roles:** _list, optional, default None_\r\n> - User roles for guest users.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on button press with a dict as a parameter.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/guest_login_buttons.JPG)\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/guest_login_google.JPG)\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/guest_login_microsoft.JPG)\r\n\r\n\r\n* You may implement the guest login widget using both Google and Microsoft OAuth2 handles.\r\n* To create the client id and client secret parameters for Google OAuth2 please refer to https://developers.google.com/identity/protocols/oauth2.\r\n* To create the client id, client secret, and tenant id parameters for Microsoft OAuth2 please refer to https://learn.microsoft.com/en-us/graph/auth-register-app-v2.\r\n* 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.\r\n\r\n### 7. Authenticating user\r\n\r\n* 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.\r\n* 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.\r\n* The optional **key** parameter for the logout button should be used with multi-page applications to prevent Streamlit from throwing duplicate key errors.\r\n\r\n```python\r\nif st.session_state['authentication_status']:\r\n authenticator.logout()\r\n st.write(f'Welcome *{st.session_state[\"name\"]}*')\r\n st.title('Some content')\r\nelif st.session_state['authentication_status'] is False:\r\n st.error('Username/password is incorrect')\r\nelif st.session_state['authentication_status'] is None:\r\n st.warning('Please enter your username and password')\r\n```\r\n\r\n> ### Authenticate.logout\r\n> #### Parameters:\r\n> - **button_name:** _str, default 'Logout'_\r\n> - Customizes the button name.\r\n> - **location:** _str, {'main', 'sidebar', 'unrendered'}, default 'main'_\r\n> - Specifies the location of the logout button. If 'unrendered' is passed, the logout logic will be executed without rendering the button.\r\n> - **key:** _str, default None_\r\n> - Unique key that should be used in multi-page applications.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on form submission with a dict as a parameter.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/logged_in.JPG)\r\n\r\n* Or prompt an unverified user to enter a correct username and password.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/incorrect_login.JPG)\r\n\r\n* 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.\r\n\r\n### 8. Creating a reset password widget\r\n\r\n* You may use the **reset_password** widget to allow a logged in user to modify their password as shown below.\r\n\r\n```python\r\nif st.session_state['authentication_status']:\r\n try:\r\n if authenticator.reset_password(st.session_state['username']):\r\n st.success('Password modified successfully')\r\n except Exception as e:\r\n st.error(e)\r\n```\r\n\r\n> ### Authenticate.reset_password\r\n> #### Parameters:\r\n> - **username:** _str_\r\n> - Specifies the username of the user to reset the password for.\r\n> - **location:** _str, {'main', 'sidebar'}, default 'main'_\r\n> - Specifies the location of the reset password widget.\r\n> - **fields:** _dict, optional, default {'Form name':'Reset password', 'Current password':'Current password', 'New password':'New password', 'Repeat password': 'Repeat password', 'Reset':'Reset'}_\r\n> - Customizes the text of headers, buttons and other fields.\r\n> - **clear_on_submit:** _bool, default False_\r\n> - Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.\r\n> - **key:** _str, default 'Reset password'_\r\n> - Unique key provided to widget to avoid duplicate WidgetID errors.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on form submission with a dict as a parameter.\r\n> #### Returns::\r\n> - _bool_\r\n> - Status of resetting the password.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/reset_password.JPG)\r\n\r\n* **_Please remember to update the config file (as shown in step 13) after you use this widget._**\r\n\r\n### 9. Creating a new user registration widget\r\n\r\n* You may use the **register_user** widget to allow a user to sign up to your application as shown below.\r\n* 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 configuration file or provide it as a parameter to the **register_user** widget.\r\n* Once they have registered, their email will be automatically removed from the **pre_authorized** list.\r\n* Alternatively, to allow anyone to sign up, do not provide a **pre_authorized** list.\r\n\r\n```python\r\ntry:\r\n email_of_registered_user, username_of_registered_user, name_of_registered_user = authenticator.register_user(pre_authorized=config['pre-authorized'])\r\n if email_of_registered_user:\r\n st.success('User registered successfully')\r\nexcept Exception as e:\r\n st.error(e)\r\n```\r\n\r\n> ### Authenticate.register_user\r\n> #### Parameters:\r\n> - **location:** _str, {'main', 'sidebar'}, default 'main'_\r\n> - Specifies the location of the register user widget.\r\n> - **pre_authorized:** _list, optional, default None_\r\n> - List of emails of unregistered users who are authorized to register. If no list is provided, all users will be allowed to register.\r\n> - **domains:** _list, optional, default None_\r\n> - 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.\r\n> - **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'}_\r\n> - Customizes the text of headers, buttons and other fields.\r\n> - **captcha:** _bool, default True_\r\n> - Specifies the captcha requirement for the register user widget, True: captcha required, False: captcha removed.\r\n> - **roles:** _list, optional, default None_\r\n> - User roles for registered users.\r\n> - **merge_username_email:** _bool, default False_\r\n> - Merges username into email field, True: username will be the same as the email, False: username and email will be independent.\r\n> - **clear_on_submit:** _bool, default False_\r\n> - Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.\r\n> - **key:** _str, default 'Register user'_\r\n> - Unique key provided to widget to avoid duplicate WidgetID errors.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on form submission with a dict as a parameter.\r\n> #### Returns:\r\n> - _str_\r\n> - Email associated with the new user.\r\n> - _str_\r\n> - Username associated with the new user.\r\n> - _str_\r\n> - Name associated with the new user.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/register_user.JPG)\r\n\r\n* **_Please remember to update the config file (as shown in step 13) after you use this widget._**\r\n\r\n### 10. Creating a forgot password widget\r\n\r\n* You may use the **forgot_password** widget to allow a user to generate a new random password.\r\n* The new password will be automatically hashed and saved in the credentials dictionary.\r\n* The widget will return the username, email, and new random password which the developer should then transfer to the user securely.\r\n\r\n```python\r\ntry:\r\n username_of_forgotten_password, email_of_forgotten_password, new_random_password = authenticator.forgot_password()\r\n if username_of_forgotten_password:\r\n st.success('New password to be sent securely')\r\n # The developer should securely transfer the new password to the user.\r\n elif username_of_forgotten_password == False:\r\n st.error('Username not found')\r\nexcept Exception as e:\r\n st.error(e)\r\n```\r\n\r\n> ### Authenticate.forgot_password\r\n> #### Parameters\r\n> - **location:** _str, {'main', 'sidebar'}, default 'main'_\r\n> - Specifies the location of the forgot password widget.\r\n> - **fields:** _dict, optional, default {'Form name':'Forgot password', 'Username':'Username', 'Captcha':'Captcha', 'Submit':'Submit'}_\r\n> - Customizes the text of headers, buttons and other fields.\r\n> - **captcha:** _bool, default False_\r\n> - Specifies the captcha requirement for the forgot password widget, True: captcha required, False: captcha removed.\r\n> - **clear_on_submit:** _bool, default False_\r\n> - Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.\r\n> - **key:** _str, default 'Forgot password'_\r\n> - Unique key provided to widget to avoid duplicate WidgetID errors.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on form submission with a dict as a parameter.\r\n> #### Returns:\r\n> - _str_\r\n> - Username associated with the forgotten password.\r\n> - _str_\r\n> - Email associated with the forgotten password.\r\n> - _str_\r\n> - New plain text password that should be transferred to the user securely.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/forgot_password.JPG)\r\n\r\n* **_Please remember to update the config file (as shown in step 13) after you use this widget._**\r\n\r\n### 11. Creating a forgot username widget\r\n\r\n* You may use the **forgot_username** widget to allow a user to retrieve their forgotten username.\r\n* The widget will return the username and email which the developer should then transfer to the user securely.\r\n\r\n```python\r\ntry:\r\n username_of_forgotten_username, email_of_forgotten_username = authenticator.forgot_username()\r\n if username_of_forgotten_username:\r\n st.success('Username to be sent securely')\r\n # The developer should securely transfer the username to the user.\r\n elif username_of_forgotten_username == False:\r\n st.error('Email not found')\r\nexcept Exception as e:\r\n st.error(e)\r\n```\r\n\r\n> ### Authenticate.forgot_username\r\n> #### Parameters\r\n> - **location:** _str, {'main', 'sidebar'}, default 'main'_\r\n> - Specifies the location of the forgot username widget.\r\n> - **fields:** _dict, optional, default {'Form name':'Forgot username', 'Email':'Email', 'Captcha':'Captcha', 'Submit':'Submit'}_\r\n> - Customizes the text of headers, buttons and other fields.\r\n> - **captcha:** _bool, default False_\r\n> - Specifies the captcha requirement for the forgot username widget, True: captcha required, False: captcha removed.\r\n> - **clear_on_submit:** _bool, default False_\r\n> - Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.\r\n> - **key:** _str, default 'Forgot username'_\r\n> - Unique key provided to widget to avoid duplicate WidgetID errors.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on form submission with a dict as a parameter.\r\n> #### Returns:\r\n> - _str_\r\n> - Forgotten username that should be transferred to the user securely.\r\n> - _str_\r\n> - Email associated with the forgotten username.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/forgot_username.JPG)\r\n\r\n### 12. Creating an update user details widget\r\n\r\n* You may use the **update_user_details** widget to allow a logged in user to update their name and/or email.\r\n* The widget will automatically save the updated details in both the credentials dictionary and re-authentication cookie.\r\n\r\n```python\r\nif st.session_state['authentication_status']:\r\n try:\r\n if authenticator.update_user_details(st.session_state['username']):\r\n st.success('Entries updated successfully')\r\n except Exception as e:\r\n st.error(e)\r\n```\r\n\r\n> ### Authenticate.update_user_details\r\n> #### Parameters\r\n> - **username:** _str_\r\n> - Specifies the username of the user to update user details for.\r\n> - **location:** _str, {'main', 'sidebar'}, default 'main'_\r\n> - Specifies the location of the update user details widget.\r\n> - **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'}_\r\n> - Customizes the text of headers, buttons and other fields.\r\n> - **clear_on_submit:** _bool, default False_\r\n> - Specifies the clear on submit setting, True: clears inputs on submit, False: keeps inputs on submit.\r\n> - **key:** _str, default 'Update user details'_\r\n> - Unique key provided to widget to avoid duplicate WidgetID errors.\r\n> - **callback:** _callable, optional, default None_\r\n> - Callback function that will be invoked on form submission with a dict as a parameter.\r\n> #### Returns:\r\n> - _bool_\r\n> - Status of updating the user details.\r\n\r\n![](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/graphics/update_user_details.JPG)\r\n\r\n* **_Please remember to update the config file (as shown in step 13) after you use this widget._**\r\n\r\n### 13. Updating the configuration file\r\n\r\n* Please ensure that the configuration 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.\r\n\r\n```python\r\nwith open('../config.yaml', 'w') as file:\r\n yaml.dump(config, file, default_flow_style=False)\r\n```\r\n\r\n## License\r\n\r\nThis project is proprietary software. The use of this software is governed by the terms specified in the [LICENSE](https://github.com/mkhorasani/Streamlit-Authenticator/blob/main/LICENSE) file. Unauthorized copying, modification, or distribution of this software is prohibited.\r\n",
"bugtrack_url": null,
"license": null,
"summary": "A secure authentication module to manage user access in a Streamlit application.",
"version": "0.4.1",
"project_urls": {
"Homepage": "https://github.com/mkhorasani/Streamlit-Authenticator"
},
"split_keywords": [
"python",
" streamlit",
" authentication",
" components"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "9328c19eae3f2b29dd4658e16d3fd3e0164af13c501493e0bc260effcfe04407",
"md5": "a5099492a550086b1524d3035799a276",
"sha256": "776136e6a6ecde1ecc9dda054a7492290199b81e19268abff307a7d508685942"
},
"downloads": -1,
"filename": "streamlit_authenticator-0.4.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a5099492a550086b1524d3035799a276",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 34747,
"upload_time": "2024-09-30T15:57:55",
"upload_time_iso_8601": "2024-09-30T15:57:55.562118Z",
"url": "https://files.pythonhosted.org/packages/93/28/c19eae3f2b29dd4658e16d3fd3e0164af13c501493e0bc260effcfe04407/streamlit_authenticator-0.4.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "f87004c24fb2d9b372edfa2261cbec3828633bc997c4bf29310ff53cf092a647",
"md5": "4b2ec75077037cae6724ba81c8767c0d",
"sha256": "d4afeec5c972d794052a5552aa570e7eb620f0cc183484a5b052304907268381"
},
"downloads": -1,
"filename": "streamlit_authenticator-0.4.1.tar.gz",
"has_sig": false,
"md5_digest": "4b2ec75077037cae6724ba81c8767c0d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 31401,
"upload_time": "2024-09-30T15:57:57",
"upload_time_iso_8601": "2024-09-30T15:57:57.258239Z",
"url": "https://files.pythonhosted.org/packages/f8/70/04c24fb2d9b372edfa2261cbec3828633bc997c4bf29310ff53cf092a647/streamlit_authenticator-0.4.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-30 15:57:57",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mkhorasani",
"github_project": "Streamlit-Authenticator",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "streamlit-authenticator"
}