Last week I mentioned that I would begin breaking down the code of the Streamlit application. However, the post would be massive, so I will be digging into smaller parts of it over the next few weeks! Enjoy!
Streamlit is a Python module that turns code into shareable web applications. I was quite surprised at how easy it was to pick this up and create an entire front-end to my data. Some Python experience is required for sure, but the learning curve on this specific module is not bad. They have fantastic documentation, and the cheat sheet will be your best friend. I promise.
Streamlit-Authenticator is a secure authentication module to validate user credentials in a Streamlit application. There is a fantastic 2-part blog series written by Mohammad Khorasani, developer of this package, that illustrates how to use this module. You can find them here and here. I believe I ended up using pieces from both parts -- in fact, the entirety of the Login page would not have been possible without his code. Thank you Mohammad!
Place streamlit-auth.yaml in the same directory that you are running your Streamlit application from:
cookie: expiry_days: 0 key: random_signature_key name: random_cookie_name credentials: usernames: player1: email: email@example.com name: Player 1 password: # paste hash from generate_password.py preauthorized: emails: - firstname.lastname@example.org - email@example.com - firstname.lastname@example.org - email@example.com
Use this to generate a hashed password to paste into your YAML. The list of email addressed under "preauthorized" allows you to control who is allowed to register via the registration page.
Streamlit-Authenticator throws errors if there is not at least 1 username in the YAML configuration. I hard-coded myself into the YAML and let everyone else use the registration page.
Because I used Streamlit's built-in session state, there is a need to "initialize" variables, which allows session information to persist across pages. I built the "initialize_session_state" function to implement this in a cleaner fashion.
You can set the value of a variable in the session state to that of an existing function in your code. I used this throughout to display pages and note input forms.
st.experimental_rerun()is used to control the flow of your application; running your script from the beginning to reflect changes in session state, variables, etc.
Before we reach the login page, we need to initialize the username variable and load the authentication YAML file into the environment. From there, I display a banner and go right into the page display logic.
### PROGRAM ### # initializes session state and loads login authentication configuration initialize_session_state(["username"]) config, authenticator = load_yml() # displays application title and sets page accordingly display_image("banner.png") if not st.session_state.username: st.session_state.runpage = app_page1 st.session_state.runpage() else: st.session_state.runpage = app_page2 st.session_state.runpage()
Because a username isn't set via the login page yet, the following function will run, displaying the login page:
def app_page1(): # displays login and registration widgets tab1, tab2 = st.tabs(["Login", "Register"]) # login tab with tab1: try: name,authentication_status,username = authenticator.login("Login","main") if authentication_status: st.session_state.runpage = "app_page2" st.experimental_rerun() elif authentication_status == False: st.error('Username/password is incorrect') elif authentication_status == None: st.warning('Please enter your username and password') except: pass # registration tab with tab2: try: if authenticator.register_user('Register', preauthorization=True): success('User registered successfully') update_yml() except Exception as e: error_message(e)
This page consist of 2 tabs, Login and Register, used for authenticating into the rest of the application and registering a new user for authentication respectively.
This tab displays a login widget, consisting of a username input, password input, and a submit button, which set values for variables in the session state. One of three things happens in this tab:
- If "authentication_status" is empty, you will be prompted to enter your information.
- After hitting submit, if your information matches something in the YAML, the "app_page2" function is run, taking you to the rest of the application.
- After hitting submit, if your information doesn't match something in the YAML, an error message will appear.
# login tab with tab1: try: name,authentication_status,username = authenticator.login("Login","main") if authentication_status: st.session_state.runpage = "app_page2" st.experimental_rerun() elif authentication_status == False: error_message('Username/password is incorrect') elif authentication_status == None: st.warning('Please enter your username and password') except: pass
This tab displays a registration widget, consisting of the fields required to be set in the YAML and a submit button. Once submitted, if there is something wrong with your input, an error message will appear. If successful, the YAML file will update.
# registration tab with tab2: try: if authenticator.register_user('Register', preauthorization=True): success('User registered successfully') update_yml() except Exception as e: error_message(e)
def error_message(text): # displays error message import time error = st.error(text) time.sleep(1) error.empty()
def initialize_session_state(variable_list): # creates empty variables in streamlit session state for variable in variable_list: if variable not in st.session_state: st.session_state[variable] = None
def load_yml(): # loads login authentication configuration import yaml from yaml.loader import SafeLoader with open(streamlit_path + "streamlit-auth.yaml") as file: config = yaml.load(file, Loader=SafeLoader) authenticator = stauth.Authenticate( config['credentials'], config['cookie']['name'], config['cookie']['key'], config['cookie']['expiry_days'], config['preauthorized'] ) return config, authenticator
def update_yml(): # updates login authentication configuration file import yaml with open(streamlit_path + "streamlit-auth.yaml", 'w') as file: yaml.dump(config, file, default_flow_style=False)
Next week, I will start digging into app_page2 and what it can accomplish!
Check out the GitHub repo below. You can also find my Twitch account in the socials link, where I will be actively working on this during the week while interacting with whoever is hanging out!