OAuth Component for Streamlit
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.
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 theauthorization_endpoint
to create a link that will direct the user to the OAuth provider so they can log in. It will send theredirect_uri
,client_id
,scope
, andstate
, 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 suppliedredirect_uri
, returning the code and state as query parameters
-st_oauth
will collect the code from the query parameters (usingst.experimental_get_query_params()
, and then use thetoken_endpoint
to exchange the code for a security token.
-st_oauth
will then use the keys from thejwks_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 codetoken_endpoint
— the URL to use to trade an authorization code for a tokenjwks_uri
— the URL to use to retrieve the JWKS signing key for the tokensredirect_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 providerclient_secret
— the client secret for the client ID, as configured in the OAuth providerscope
— the OAuth scope to use, as configured in the OAuth provideraudience
— the audience as configured in the OAuth provideridentity_field_in_token
— which field in the returned token contains the identity (usually, it issub
orupn
). This is the field that will be returned from thest_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()
).