How to block execution until user inputs into chat_input

I need help architecting a Streamlit application that lets the user authenticate with Microsoft’s Single Sign On (SSO) flow. Specifically, the application displays a URL using st.write, for the user to visit, retrieve an authentication token, type it into the chat_input window, and then authenticate with the Microsoft SSO API.

The problem I’m running into is that because the st.chat_input() function is non-blocking, the authentication function is run before the authentication token is entered by the user. Below is a snippet that outlines how I’m trying to develop it.

The place where I could use some help is how to implement the [WAIT UNTIL THE USER RETURNS A VALUE] section. Or otherwise, if I’m using the Streamlit paradigm completely wrong, any architecture advice would be great.

st.write("Please visit (this link) and enter your authentication information in the text box below.")
message = {"role": "assistant", "content": response} 
st.session_state.messages.append(message)

if token := st.chat_input():
   st.session_state.messages.append({"role": "user", "content": token})
   with st.chat_message("user"):
      st.write(prompt)

[WAIT UNTIL THE USER RETURNS A VALUE]

response = myAuthenticationFuncion(token)
return(response)
1 Like

There are errors in your sample code.

One thing to remember, the line:

if token := st.chat_input():

will let us pass only once. If there are function, etc that triggers a code rerun from top to bottom, we cannot pass through it again.

I make a sample code that you can run and experiment with.

import streamlit as st
from streamlit import session_state as ss


# Step 1: Create a session variable to store the response.
# The reason why we are doing this is because we can access
# the response anywhere in the code. The initial value is None.
if 'response' not in ss:
    ss.response = None


# Step 2: Define a function to authenticate the user.
def authenticate_user(token):
    """Save the response to session variable."""
    response = f'hello {token}'  # you real stuff here

    # Save a sample response to session variable.
    ss.response = f'Your token is {token}, and my response is {response}.'


# Step 3: Show the link to user
st.write("Please visit (this link) and enter your authentication \
         information in the text box below.")

# Step 4: Ask the user to input the token.
# Careful with the statement, it will only allow us to pass once.
if token := st.chat_input():

    # This will trigger a code rerun from top to bottom and as a result
    # we cannot enter here again because of that
    # "if token := st.chat_input():" above.
    # That is why we save the response in session variable.
    # There are techniques to re-enter here but will not do so because
    # I am not familiar enough of your app.
    authenticate_user(token)

# Step 5: Show the contents of response.
st.write(f'response: {ss.response}')
1 Like

Thank you @ferdy for your in-depth and quick response. This helps me get closer to the answer, but doesn’t reflect how the API’s authenticate function works. Below I’ve updated the pseudocode to reflect how the API’s authenticate function works. Specifically, it shows the issue in that I need the user to type in the token in step 2.2, which occurs inside the API’s authentication function.

So, my question is whether there is a way to send the input from st.chat_input() into the authenticate_user() function when it is inputted in Step 2.2.

Thanks again for your help!

import streamlit as st
from streamlit import session_state as ss


# Step 1: Create a session variable to store the response.
# The reason why we are doing this is because we can access
# the response anywhere in the code. The initial value is None.
if 'response' not in ss:
    ss.response = None


# Step 2: Define a function to authenticate the user.
def authenticate_user():
   # Step 2.1: Show the link to user to retrieve the token
   st.write("Please visit (this link) and enter your authentication \
            information in the text box below.")

   # Step 2.2 Ask the user to return the token received from the link using the text box.

    """Save the response to session variable."""
    response = token  # the token received here

    # Step 2.3 Run function to authenticate here
    authenticate_token(token)

    # Save a sample response to session variable.
    ss.response = f'Your token is {token}, and my response is {response}.'

# Step 4: Ask the user to input the token.
# Careful with the statement, it will only allow us to pass once.
if token := st.chat_input():

    # This will trigger a code rerun from top to bottom and as a result
    # we cannot enter here again because of that
    # "if token := st.chat_input():" above.
    # That is why we save the response in session variable.
    # There are techniques to re-enter here but will not do so because
    # I am not familiar enough of your app.
    authenticate_user()

# Step 5: Show the contents of response.
st.write(f'response: {ss.response}')
1 Like

This makes little sense to me

ss.response = f'Your token is {token}, and my response is {response}.'

but response is the same as token.

Also, what does calling authenticate_token do? We are not looking at the return value so it must have some side effect but it is not clear what it is and how it would be relevant to your app.

2 Likes

Thanks @Goyo for your quick reply. To answer your question about the authentication_token() function, it’s a function that provides a URL, that the user must visit to authenticate and receive an authentication token. Then, the function returns the authentication token to complete the authentication.

The following line, isn’t required, so I won’t try to explain to focus on the key issue:
ss.response = f'Your token is {token}, and my response is {response}.

1 Like

On that we will use a text input widget.

# Step 2.2 Ask the user to return the token received
# from the link using the text box.
st.text_input('Input token', key='token', on_change=authenticate_token)

Utilize the on_change parameter to call the function authenticate_token which is defined below.

# Step 1.1: Define a function to authenticate the token.
def authenticate_token():
    """Generates response based on token.
    
    This function is called whenever there are changes to text input
    widget at step 2.2 to get the token. The token is taken based from
    the key of that text input widget.
    """
    token = ss.token
    is_token_ok = True  # is_token_ok = is_valid_token(token)
    if is_token_ok:
        response = f'hello {token}'
        ss.response = f'Your token is {token}, and my response is {response}.'
    else:
        ss.response = None

Full revised sample code

import streamlit as st
from streamlit import session_state as ss


# Step 1: Create a session variable to store the response.
# The reason why we are doing this is because we can access
# the response anywhere in the code. The initial value is None.
if 'response' not in ss:
    ss.response = None


# Step 1.1: Define a function to authenticate the token.
def authenticate_token():
    """Generates response based on token.
    
    This function is called whenever there are changes to text input
    widget at step 2.2 to get the token. The token is taken based from
    the key of that text input widget.
    """
    token = ss.token
    is_token_ok = True  # is_token_ok = is_valid_token(token)
    if is_token_ok:
        response = f'hello {token}'
        ss.response = f'Your token is {token}, and my response is {response}.'
    else:
        ss.response = None
        

# Step 2: Define a function to authenticate the user.
def authenticate_user():
    # Step 2.1: Show the link to user to retrieve the token
    st.write("Please visit (this link) and enter your authentication \
              information in the text box below.")

    # Step 2.2 Ask the user to return the token received
    # from the link using the text box.
    st.text_input('Input token', key='token', on_change=authenticate_token)


# Step 4: Prompts the user.
# Careful with the statement, it will only allow us to pass once.
if prompt := st.chat_input():

    # This will trigger a code rerun from top to bottom and as a result
    # we cannot enter here again because of that
    # "if token := st.chat_input():" above.
    # That is why we save the response in session variable.
    # There are techniques to re-enter here but will not do so because
    # I am not familiar enough of your app.
    authenticate_user()

# Step 5: Show the contents of response.
st.write(f'response: {ss.response}')
2 Likes

Sorry, I don’t get this. In your example the URL is provided outside of authenticate_token() and before calling it.

2 Likes

Thank you @ferdy, this implementation worked for me. I am tagging your post as the solution to help others in the future.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.