This tutorial was written by Gideon Idoko, a solution-driven software engineer and technical writer. Connect with him on his website or X to see more of his work!
With over a 96 percent increase in interest in the past year, Streamlit has rapidly gained popularity among data scientists and developers as a preferred tool for constructing interactive data applications. Its simplicity and powerful capabilities enable users to effortlessly transform data scripts into web apps that can be easily shared with others. However, as the usage of Streamlit applications continues to rise, there is an increasing need for effective user authentication and single sign-on (SSO) techniques.
User authentication is essential for safeguarding sensitive data and guaranteeing that only authorized users can access specific features of an app. SSO streamlines the login process, enabling users to authenticate once and effortlessly access multiple applications without the need to log in repeatedly.
In this article, you’ll learn how to use Descope, a flexible authentication and user management platform, to add authentication, SSO, and role-based access control to your Streamlit app.
Prerequisites
Before diving into the integration process, ensure you understand the basics of Python and Streamlit. You also need a Descope account, but don’t worry if you don’t have one yet—you’ll learn how to set one up shortly. The free tier will be sufficient for you to follow along.
Setting up Descope
Initially, go to the Descope sign-up page and register for an account. After that, you are taken to your Descope console. Create a new project by selecting + Project
from the drop-down menu on the top-left part of the console. The key functionalities of the console include creating projects, managing user roles, and configuring authentication flows. It also lets you configure your project settings in Settings > Project:
The Settings
page shows your project ID, among other information. The project ID uniquely identifies your project within the Descope platform. This ID is essential for the integration process as it’s required to connect your application to Descope. Make sure that you don’t store it directly in your codebase. Rather, employ environment variables to ensure the security of your credentials. Later, you will discover a good way to securely store and use the project ID in your Streamlit app.
Creating a Streamlit app
Now, let’s create a simple Streamlit app. Begin by creating a directory and, optionally, a virtual environment for the app on your local machine.
Install Streamlit with the following command:
pip install streamlit
Create an app.py
file in your app directory and paste the following snippet:
import streamlit as st
st.title("Demo App")
st.write("This is a demo app with Descope-powered authentication and SSO")
This creates a simple Streamlit app with title and text elements. Run the app using streamlit run app.py
. Streamlit then spins up the app on port 8051
in your browser.
You should get an output like this:
Implementing OAuth social logins with Descope
OAuth social logins enhance the user experience by simplifying registration and login processes. It leverages trusted platforms, such as Google or GitHub, to log users in. This system reduces security and increases conversion rates by lowering barriers to entry.
Configuring Descope as a federated identity provider for Streamlit
This section walks you through setting up Descope as a federated identity provider (IdP) for Streamlit and seamlessly integrating Google login with the application you created earlier.
Navigate to Build > Authentication Methods > Social Login (OAuth/OIDC) to see the various supported social logins that are available. Then select Google
:
Note: The Descope console provides an authentication account for all common social logins. There’s also an option to specify a different account for authentication if you prefer.
Let’s proceed with the Descope authentication account for Google and specify the redirect URL. The redirect URL is the location to send a user to after successful OAuth authentication. In this case, you want the user to return to the Streamlit app, so include the app’s URL, http://localhost:8051
, in the configuration provided, like this:
Remember, you can also provide the redirect URL programmatically.
Integrating Descope with your Streamlit app as its OAuth provider
Your Streamlit app needs to communicate with Descope, so install its Python SDK to facilitate the interaction:
pip install descope
You need a project ID to initialize Descope, so copy yours from the console settings, create a .streamlit/secrets.toml
file in the root directory of your app, and include the project ID, like this:
DESCOPE_PROJECT_ID = "XXXXX"
Initialize Descope in your app:
import streamlit as st
from descope.descope_client import DescopeClient
DESCOPE_PROJECT_ID = str(st.secrets.get("DESCOPE_PROJECT_ID"))
descope_client = DescopeClient(project_id=DESCOPE_PROJECT_ID)
st.title("Demo App")
st.write("This is a demo app with Descope-powered authentication and SSO")
Next, update the code to create a button that starts the OAuth flow using the descope_client.oauth.start()
method:
# … collapsed repeated code
descope_client = DescopeClient(project_id=DESCOPE_PROJECT_ID)
st.warning("You're not logged in, pls login")
with st.container(border=True):
if st.button("Sign In with Google", use_container_width=True):
oauth_response = descope_client.oauth.start(
provider="google", return_url="http://localhost:8501"
)
url = oauth_response["url"]
# Redirect to Google
st.markdown(
f'<meta http-equiv="refresh" content="0; url={url}">',
unsafe_allow_html=True,
)
st.title("Demo App")
# …
Descope generates a Google sign-in URL upon starting the OAuth flow. The preceding code then redirects the user to that URL for authentication.
Your Streamlit app should now look like this:
Click the Sign In with Google
button, and you are redirected to the app with a code
query parameter after successfully authenticating via Google. This flow is called the authorization code flow. Shortly, you’ll exchange the code sent back to the app with a token to authenticate your users.
You can leverage the Streamlit st.query_params
method to pick the code from the URL state and the descope_client.sso.exchange_token()
method to exchange the code for a token. Descope also provides other information, like the user’s data and a refresh token that refreshes the short-lived token. With the Streamlit session state, you can persist this information across multiple runs and conditionally display the app’s content only if the token is available in the session state.
To persist the tokens and user data, update your code like this:
import streamlit as st
from descope.descope_client import DescopeClient
from descope.exceptions import AuthException
DESCOPE_PROJECT_ID = str(st.secrets.get("DESCOPE_PROJECT_ID"))
descope_client = DescopeClient(project_id=DESCOPE_PROJECT_ID)
if "token" not in st.session_state:
# User is not logged in
if "code" in st.query_params:
# Handle possible login
code = st.query_params["code"]
# Reset URL state
st.query_params.clear()
try:
# Exchange code
with st.spinner("Loading..."):
jwt_response = descope_client.sso.exchange_token(code)
st.session_state["token"] = jwt_response["sessionToken"].get("jwt")
st.session_state["refresh_token"] = jwt_response["refreshSessionToken"].get(
"jwt"
)
st.session_state["user"] = jwt_response["user"]
st.rerun()
except AuthException:
st.error("Login failed!")
st.warning("You're not logged in, pls login")
with st.container(border=True):
if st.button("Sign In with Google", use_container_width=True):
oauth_response = descope_client.oauth.start(
provider="google", return_url="http://localhost:8501"
)
url = oauth_response["url"]
# Redirect to Google
st.markdown(
f'<meta http-equiv="refresh" content="0; url={url}">',
unsafe_allow_html=True,
)
else:
# User is logged in
try:
with st.spinner("Loading..."):
jwt_response = descope_client.validate_and_refresh_session(
st.session_state.token, st.session_state.refresh_token
)
# Persist refreshed token
st.session_state["token"] = jwt_response["sessionToken"].get("jwt")
st.title("Demo App")
st.write("This is a demo app with Descope-powered authentication and SSO")
st.subheader("Welcome! you're logged in")
if "user" in st.session_state:
user = st.session_state.user
st.write("Name: " + user["name"])
st.write("Email: " + user["email"])
if st.button("Logout"):
# Log out user
del st.session_state.token
st.rerun()
except AuthException:
# Log out user
del st.session_state.token
st.rerun()
Now, your app’s content shows only when a user has been authenticated:
Implementing SAML SSO with Descope
The Security Assertion Markup Language (SAML) is the open standard for exchanging authentication and authorization data between these applications, often classified as either IdPs, like Okta, or service providers (SPs), like your Streamlit app. SAML SSO is a user authentication process that uses SAML to allow users to access multiple applications with a single login. It’s particularly beneficial for organizations that have many applications and want to simplify the login process for their users.
In the world of SSO, businesses or applications that use your app are associated with SSO tenants. A tenant allows you to group and manage users, permissions, and other aspects of a business within your application.
Configuring Okta as the IdP and Descope as the SP
This example uses Okta IdP for user account management. Let’s configure it to enable users to log in to your Streamlit app, which acts as the SP.
The initial step is to create a tenant. Go to the Tenants page in your Descope console and click + Tenant
:
Navigate to the tenant Authentication Methods
section, choose the SAML
protocol, and enter your email domain in the SSO Domains
field under the Tenant Details
settings:
Because Descope communicates with Okta on behalf of your Streamlit app, these apps need a way to establish a secure and trusted connection. This is where a metadata XML document comes in. You need to share Descope’s metadata with Okta and vice versa. Luckily, the Okta Descope integration streamlines the setup of Okta as an IdP.
Visit your Okta account and click Admin
to access your admin dashboard:
Select Applications > Applications
in the sidebar of your admin dashboard and click Browse App Catalog
to explore a comprehensive list of available integrations:
Look for “descope” using the search function and seamlessly incorporate the Descope integration. A new Okta app is created once the Descope integration is added:
Specify a label for the new Okta app in the General Settings
tab:
Switch to the Assignments
tab and assign users to your Okta app to specify the users that will have access to your Streamlit app via SSO:
Switch to the Sign-On Options
tab and select SAML 2.0
. Copy the Metadata URL
you see and paste it into the provided field for your tenant’s SSO Configuration
. Copy the ACS URL
and Entity ID
values from the same configuration section into the Advanced Sign-on Settings
on Okta:
Expand and update the Attribute Statements
and map the names firstName
, lastName
, email
, and uid
to the values user.firstName
, user.lastName
, user.email
, and user.id
, respectively. An attribute statement is part of SAML that provides supplementary details about a user. The mapping here specifies how these user details are communicated to Descope:
Head back over to Descope and update your tenant’s SSO Mapping
settings to remap the attributes from Okta to those of Descope, like this:
This marks the end of the configuration.
It’s worth noting that Descope provides a self-service SSO configuration tailored for multi-tenant environments. This solution allows each tenant to have a unique SSO configuration and empowers tenant admins to manage their Descope SSO settings directly through your application.
To use this feature, embed the out-of-the-box sso-config
flow into your application’s administration interface or generate a configuration link for tenant admins. Refer to the Descope documentation for detailed guidance.
Implementing the SSO flow in Streamlit
Let’s add another button under the Sign In with Google
button that starts the SSO flow when a user clicks it. You need your tenant’s ID to trigger the SSO flow, so go to the tenant settings, copy the ID, and paste it into your .streamlit/secret.toml
file with the DESCOPE_TENANT_ID
key.
Update the st.container
element in your code to include the button, like this:
# … collapsed repeated code
TENANT_ID = str(st.secrets.get("DESCOPE_TENANT_ID")) # Get tenant ID from secret
# …
with st.container(border=True):
# … google button here
if st.button("Sign in with SSO", use_container_width=True):
sso_response = descope_client.sso.start(
tenant=TENANT_ID, return_url="http://localhost:8501"
)
url = sso_response["url"]
# Redirect to Okta
st.markdown(
f'<meta http-equiv="refresh" content="0; url={url}">',
unsafe_allow_html=True,
)
# …
Your Streamlit app should now look like this:
Click the Sign In with SSO
button, and you’ll be taken to Okta for verification. Verify with one of the users you assigned earlier, and afterward, you are redirected to your Streamlit app with a code query parameter like in the OAuth flow. Since you’ve already implemented the code exchange, you are signed in automatically:
Managing authorization and user privileges
While authentication verifies the user’s identity, authorization controls their access to resources and functionalities based on predefined roles and permissions. The Descope console has features to manage user roles and permissions, allowing you to control access within your Streamlit app. Once a user logs in, Descope can provide information about their roles and permissions, which can be used to enforce access control in your application.
All roles and permissions are on the Authorization page. A Tenant Admin
role is created by default, so let’s assign that to a user and conditionally display admin-specific content in your Streamlit app. Go to the Users page, click the kebab menu of the user you want to assign, and click Edit
:
This shows you a modal; select Tenant Admin
in the Roles
field and click Save
to assign the admin role to the user:
Update your code to show an admin indicator:
# … collapsed repeated code
if "user" in st.session_state:
# …
if "Tenant Admin" in user["roleNames"]:
# Show admin-specific content
st.success("ADMIN", icon="🤖")
# …
This should now be the view of the admin user when they’re signed in:
This approach guarantees that only authorized users can access specific features, thus enhancing the security and integrity of your app.
You can find the full code on GitHub.
Conclusion
In this article, you learned how to add authentication and SSO functionalities to your Streamlit app using Descope. You also learned the importance of securing your applications. Moreover, you learned the step-by-step process of setting up Descope, integrating OAuth social logins, implementing SAML SSO, and managing user roles and permissions for effective access control.
To explore how Descope can help you add secure, frictionless authentication to your apps, sign up for the Free Forever tier. Have questions about the platform? Book time with the team for a dedicated demo.