OAuth Component for Streamlit

Brian Hess
Streamlit
Published in
5 min readApr 6, 2023

One of the common requests I get when talking with customers developing (or planning to develop) with Streamlit is how to protect access to Streamlit properly. One of the most common requests in this vein is the ability to use an Identity Provider (IdP) to authenticate and authorize access to the Streamlit app.

One solution is restricting access to the Streamlit app at the network level. For example, some application load balancers (such as Amazon Elastic Load Balancer) integrate with IdPs that authenticate the visitor before routing the network traffic to the application. I discussed this in a previous blog post.

However, sometimes you want the application itself to integrate with the IdP. Moreover, it would be nice to get the authenticated user's identity inside our Streamlit to provide a customized experience (or even just for usage tracking).

This led me to investigate how to build a Streamlit component that would handle integration with an OAuth provider, and the result is st_oauth. (Huge shout-out to Mike Mitrowski for helping me through the details here — I couldn’t have done it without you!)

Now, with code as simple as this…

import streamlit as st
from st_oauth import st_oauth

st.markdown("## This (and above) is always seen")
id = st_oauth('myoauth', 'Click to login via OAuth')
st.markdown("## This (and below) is only seen after authentication")

…we “gate” the entire Streamlit app to stop until the user is authenticated. Once authenticated, the identity (as configured in OAuth) is returned.

Prior to authentication
Post-authentication

I built st_oauth to integrate with identity providers via OAuth. I have tested it with Okta, specifically, but the app obeys the normal OAuth flows and should work with any OAuth provider.

Some Details

The basic flow that I have implemented is as follows:

  • st_oauth checks if the token is cached in session state
  • If it is, then
    - st_oauth validates that the token is still valid (e.g., it has not expired)
    - st_oauth returns the identity from the field in the token, and the Streamlit app can continue
  • If it is not, then
    - st_oauth uses the authorization_endpoint to create a link that will direct the user to the OAuth provider so they can log in. It will send the redirect_uri, client_id, scope, and state, and request a code in response
    - the user clicks the link and logs in at the OAuth provider’s website, and the OAuth provider will redirect back to the supplied redirect_uri, returning the code and state as query parameters
    - st_oauth will collect the code from the query parameters (using st.experimental_get_query_params(), and then use the token_endpoint to exchange the code for a security token.
    - st_oauth will then use the keys from the jwks_uri to validate that the token is valid.
    - st_oauth will extract the identity from the field in the token, cache it in session state, and return it, and the Streamlit app can continue

Setup

To set up the identity provider with OAuth, create a new application client in your provider. You must supply a “redirect_uri,” where the identity provider will redirect after validation. For st_oauth, provide the URL for your Streamlit application. For example, if you are running on your laptop (perhaps, during development), you would use http://localhost:8501.

The set of OAuth configuration parameters we need for st_oauth are as follows. You can get them from your OAuth provider:

  • authorization_endpoint — the URL to use to get an authorization code
  • token_endpoint — the URL to use to trade an authorization code for a token
  • jwks_uri — the URL to use to retrieve the JWKS signing key for the tokens
  • redirect_uri — the URL that is configured in the OAuth provider as the redirect URL (it should be the URL of the Streamlit app itself)
  • client_id — the client ID, as configured in the OAuth provider
  • client_secret — the client secret for the client ID, as configured in the OAuth provider
  • scope — the OAuth scope to use, as configured in the OAuth provider
  • audience — the audience as configured in the OAuth provider
  • identity_field_in_token — which field in the returned token contains the identity (usually, it is sub or upn). This is the field that will be returned from the st_oauth() call.

To configure st_oauth we need a set of OAuth configuration parameters (as discussed above) and a prompt to display the login link.

Specifying the OAuth Configuration

There are three ways to pass the OAuth configuration parameters in. First, you can provide a dictionary that contains the configuration parameters:

config_dict = {'authorization_endpoint': ….}
id = st_oauth(config_dict)

Second, you can store the parameters in the secrets file (.streamlit/secrets.toml):

[myoauth]
authorization_endpoint = "<OAUTH AUTH ENDPOINT usually ending in /v1/authorize>"
token_endpoint = "<OAUTH TOKEN ENDPOINT usually ending in /v1/token>"
jwks_uri = "<OAUTH JWKS ENDPOINT>"
redirect_uri = "<REDIRECT URI - this Streamlit's location>"
client_id = "<OAUTH CLIENT ID>"
client_secret = "<OAUTH CLIENT SECRET>"
scope = "<OAUTH SCOPE>"
audience = "<OAUTH AUDIENCE>"
identity_field_in_token = "<OAUTH TOKEN ID FIELD - sub or upn>"

Then you pass in the name of the block of parameters in st.secrets:

id = st_oauth(‘myoauth’)

Lastly, by default, st_oauth will look in st.secrets for the block for parameters named oauth. So, if you store the configuration in the secrets file under the label “oauth”, like this:

[oauth]
authorization_endpoint = "<OAUTH AUTH ENDPOINT usually ending in /v1/authorize>"
token_endpoint = "<OAUTH TOKEN ENDPOINT usually ending in /v1/token>"
jwks_uri = "<OAUTH JWKS ENDPOINT>"
redirect_uri = "<REDIRECT URI - this Streamlit's location>"
client_id = "<OAUTH CLIENT ID>"
client_secret = "<OAUTH CLIENT SECRET>"
scope = "<OAUTH SCOPE>"
audience = "<OAUTH AUDIENCE>"
identity_field_in_token = "<OAUTH TOKEN ID FIELD - sub or upn>"

Then you can call st_oauth with no parameters:

id = st_oauth()

Specifying the Link Text

The other parameter you can pass into st_oauth is the text to use in the link. By default, the text is Login via OAuth, but you can customize this:

id = st_oauth(label='Click to login via Okta')

Multipage Apps

st_oauth works with multipage apps. You must include the “gate” at the top of each page. The user will receive the login link if they have not logged in. Once they complete the OAuth login, they will be redirected to the Streamlit app’s main page (the redirect URI configured in the OAuth provider).

Query Parameters

st_oauth does work with query parameters. If you navigate to Streamlit and have query parameters set, st_oauth will save the parameters that were passed in (leveraging st.experimental_get_query_params()), and when the OAuth provider (upon successful login) redirects back to Streamlit, st_oauth will re-add the query parameters back to the URL (leveraging st.experimental_set_queyr_params()).

--

--

Brian Hess
Streamlit

I’ve been in the data and analytics space for over 25 years in a variety of roles. Essentially, I like doing stuff with data and making data work.