back arrowBack to Blog

Developers

Add Authentication and SSO to Your Streamlit App

Streamlit tutorial thumbnail

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:

Descope Streamlit image 1
Fig: Descope console project settings

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:

Descope streamlit image 2
Fig: Simple Streamlit app

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:

Descope streamlit image 3
Fig: Select Google social login

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:

Descope streamlit image 4
Fig: Add redirect

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:

Descope streamlit image 5
Fig: Streamlit app with a Google button

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:

Descope streamlit image 6
Fig: Authenticated Streamlit app

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:

Descope streamlit image 7
Fig: Create a Descope 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:

Descope streamlit image 8
Fig: Select an SSO protocol

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:

Descope streamlit image 9
Fig: Okta dashboard

Select Applications > Applications in the sidebar of your admin dashboard and click Browse App Catalog to explore a comprehensive list of available integrations:

Descope streamlit image 10
Fig: Okta admin dashboard

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:

Descope streamlit image 11
Fig: Add Okta Descope integration

Specify a label for the new Okta app in the General Settings tab:

Descope streamlit image 12
Fig: Okta app general settings

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:

Descope streamlit image 13
Fig: Assign users to the Okta app

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:

Descope streamlit image 14
Fig: Okta Advanced Sign-on Settings

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:

Descope streamlit image 15
Fig: Update Okta attribute mapping

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:

Descope streamlit image 16
Fig: Descope SSO mapping

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:

Descope streamlit image 17
Fig: Streamlit app final login look

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:

Descope streamlit image 18
Fig: Streamlit SSO authenticated view

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:

Descope streamlit image 19
Fig: Edit Descope user

This shows you a modal; select Tenant Admin in the Roles field and click Save to assign the admin role to the user:

Descope streamlit image 20
Fig: Assign admin role to a 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:

Descope streamlit image 21
Fig: Admin view

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.