mkhorasani / Streamlit-Authenticator

A secure authentication module to manage user access in a Streamlit application.
Other
1.66k stars 258 forks source link

Authentication with [experimental] guest_login can't keep user after refresh #226

Closed informatica92 closed 1 month ago

informatica92 commented 1 month ago

I'm trying to create a very simple application based only on Google authentication. At the moment the code is the following:

main.py

import streamlit as st
import streamlit_authenticator as stauth

import yaml
from yaml.loader import SafeLoader

with open('config.yaml') as file:
    config = yaml.load(file, Loader=SafeLoader)

authenticator = stauth.Authenticate(
    config['credentials'],
    config['cookie']['name'],
    config['cookie']['key'],
    config['cookie']['expiry_days']
)

# st.write(st.session_state)
# st.write(config)

if st.session_state['authentication_status']:
    st.write(f'Welcome *{st.session_state["name"]}*')
elif st.session_state['authentication_status'] is None:
    user = authenticator.experimental_guest_login('Login with Google', provider='google', oauth2=config['oauth2'])

config.yaml

cookie:
  expiry_days: 30
  key: some_signature_key
  name: some_cookie_name
credentials:
  usernames:
oauth2: # Optional
  google: # Follow instructions: https://developers.google.com/identity/protocols/oauth2
    client_id: 429....googleusercontent.com
    client_secret: GO...94
    redirect_uri: http://localhost:8501

image

When I run this script the first time (starting wit no cookies) everything works as expected and after the google account selection, the page appears like this: image

But now, if I try to refresh the page, what I get is: image streamlit_authenticator.utilities.exceptions.LoginError: User not authorized

The cookies are still there but the user is not authenticated with the above error

The app is running locally at the moment (localhost:8501).

Have you ever experienced such a behavior? Is there a way to fix? (I know it is experimental)

mkhorasani commented 1 month ago

Hi @informatica92, this is because you are not re-saving the config file as shown in step 13 of the readme file. This is required so that when you refresh the webpage and the re-authentication cookie is read, it can find the details of the guest user in the config file.

informatica92 commented 1 month ago

Thank @mkhorasani for the fast reply. I tried as you said but the issue is still there.

The code is now:

import streamlit as st
import streamlit_authenticator as stauth

import yaml
from yaml.loader import SafeLoader

with open('config.yaml') as file:
    config = yaml.load(file, Loader=SafeLoader)

authenticator = stauth.Authenticate(
    config['credentials'],
    config['cookie']['name'],
    config['cookie']['key'],
    config['cookie']['expiry_days']
)

st.write(st.session_state)
# st.write(config)

st.write(st.session_state['authentication_status'])

if st.session_state['authentication_status'] is not None:
    print("authentication_status is NOT None")
    st.write(f'Welcome *{st.session_state["name"]}*')
    with open('config.yaml', 'w') as file:  # NEW SECTION AS YOU ASKED
        yaml.dump(config, file, default_flow_style=False)
else:
    print("authentication_status None")
    authenticator.experimental_guest_login('Login with Google', provider='google', oauth2=config['oauth2'])

The issue persists also because the config file hasn't changed that much. It's now

cookie:
  expiry_days: 30
  key: some_signature_key
  name: some_cookie_name
credentials:
  usernames: {}
oauth2:
  google:
    client_id: 42...nt.com
    client_secret: GO...94
    redirect_uri: http://localhost:8501
preauthorized: null

It looks that the only updated parts are: usernames: {} and preauthorized: null in which the explicit empty value (empty dict and None/null) have been added

mkhorasani commented 1 month ago

Please add the save config file at the end of your script outside of the if/else statement.

informatica92 commented 1 month ago

It works! Thanks a lot! Personally I'd highlight more the need to persist these changes. I don't know, some main flow like Register (via form or via guest) -> Usage -> Update the config.

Also, reading from the doc, it seems that is something to call only "anytime the credentials are updated or whenever the experimental_guest_login, reset_password, register_user, forgot_password, or update_user_details". The tool is great! It deserves a good documentation as well

mkhorasani commented 1 month ago

It works! Thanks a lot! Personally I'd highlight more the need to persist these changes. I don't know, some main flow like Register (via form or via guest) -> Usage -> Update the config.

Also, reading from the doc, it seems that is something to call only "anytime the credentials are updated or whenever the experimental_guest_login, reset_password, register_user, forgot_password, or update_user_details". The tool is great! It deserves a good documentation as well

Sure! Will look into this. Thank you for your feedback.