Skip to main contentArrow Right

Table of Contents

This tutorial was written by Kevin Kimani, a passionate developer and technical writer who enjoys explaining complicated concepts in a simple way. Connect with him on his GitHub or X to see more of his work!


Gradio is an open source Python package that allows you to create web-based interfaces for AI models, APIs, or any Python function. Its simplicity and flexibility make it a popular choice among developers who want to quickly prototype and deploy web-based interfaces without worrying about frontend development.

Secure authentication methods are needed for these applications to help prevent sensitive data and models from being exposed and ensure that only authorized users can access certain features. Descope is an authentication and user management platform that simplifies the process of adding secure authentication to your applications. It offers a range of authentication methods and out-of-the-box support for OAuth 2.0 and OpenID Connect (OIDC), which makes it easy to implement authentication in your Gradio app.

In this guide, you’ll learn how to integrate Descope authentication and single sign-on (SSO) into a Gradio application. You’ll add basic auth to your Gradio application using magic links and social login. You’ll also implement SSO into the application by configuring Descope to use Okta as an identity provider(IdP).

Why is adding SSO important for these types of apps?

Many Gradio applications are used for business-to-business (B2B) purposes, where different organizations need secure access to their machine-learning models or APIs. In such cases, SSO is essential for several reasons:

  • Seamless access: Employees can log in using their company credentials instead of managing separate usernames and passwords.

  • Improved security: SSO reduces password fatigue and minimizes security risks associated with weak or reused passwords.

  • Better user management: IT administrators can enforce access policies, control permissions, and revoke access centrally through identity providers like Okta or Azure AD.

Prerequisites

To follow this tutorial, you need the following:

Creating a Gradio application

To keep the focus on implementing authentication and SSO, the tutorial uses a prepared starter template that you’ll build on. To clone it to your local machine, execute the command below:

git clone --single-branch -b starter-template https://github.com/kimanikevin254/descope-gradio-auth-sso.git

The Gradio application you just cloned is mounted within a FastAPI application. This setup is necessary because Gradio only supports authentication with external OAuth providers, such as Descope, when it is mounted inside a FastAPI app.

Here’s an overview of the most important files in this project:

  • app/core/config.py: Loads environment variables and sets application configurations using Pydantic.

  • app/ui/gradio_apps/: Contains three simple Gradio applications:

    • admin_dashboard.py: Displays user info and a logout button. In a real-world app, this would handle admin-specific features.

    • user_dashboard.py: Similar to the admin dashboard but for regular users. This is where you would implement non-admin functionalities.

    • login_page.py: A simple login page that prompts users to log in with a login button.

  • app/ui/gradio_mount.py: Defines the GradioMounter class, which mounts all Gradio apps to the FastAPI application.

  • .env.example: Defines the environment variables you will require in this project.

You’ll explore the other files later.

With the files cloned to your local machine, you can move on to setting up the application. Start by creating a virtual environment, activating it, and installing all the dependencies:

python3 -m venv .venv # Create the virtual environment

source .venv/bin/activate #Activate it

pip install -r requirements.txt # Install dependencies

Rename the .env.example file to .env:

mv .env.example .env

Run the application using the command fastapi dev app/main.py and navigate to http://localhost:8000/auth/ to view the login page created using Gradio:

Fig: Login page
Fig: Login page

To view the admin dashboard, navigate to http://localhost:8000/gradio/admin:

Fig: Admin dashboard
Fig: Admin dashboard

To view the user dashboard, navigate to http://localhost:8000/gradio/user:

Fig: User dashboard
Fig: User dashboard

As you can see, all the dashboards are publicly available, which poses some security risks. In the next sections, you’ll add authentication to protect them and authorization to make sure that only users with the appropriate role can access the admin dashboard.

Setting up Descope

To integrate Descope as an external OAuth provider for your Gradio application, you’ll need to apply some configurations in your Descope console.

Open your Descope console and create a new project by clicking the project dropdown and selecting + Project:

Fig: Creating a new project
Fig: Creating a new project

Provide a project name on the Create project form and click Create:

Fig: Providing project details
Fig: Providing project details

You need to define a flow that will support the authentication methods you require for this project: magic links, social login, and SSO. To simplify the process, this tutorial uses a preconfigured flow included in the starter template. You’ll find it in the root folder as sign-up-or-in.json. You only need to import it into Descope. To do this, navigate to Flows > sign-up-or-in and select Import flow / Export flow > Import flow inside the flow editor. This will allow you to upload the flow from your local machine:

Fig: Importing a flow
Fig: Importing a flow

The flow you just imported offers three login options on the welcome screen: magic link, SSO, and social login. If a user chooses the magic link, they receive an email with a link. Clicking the link prompts new users to provide extra details while existing users get a JWT and complete the process. For social login or SSO, users are redirected to their provider, and after successful authentication, they receive a JWT, ending the flow.

Authenticating with Descope

With Descope fully set up, you can now integrate it as a custom OAuth provider for your Gradio application. You’ll need to configure an OIDC app in the Descope dashboard to obtain the necessary endpoints and credentials for the authorization code flow.

Here are the required credentials:

  • Client ID: A unique public identifier assigned to the application

  • Client secret: A confidential key shared only between the application and the authorization server

  • Authorization endpoint: The URL that starts the authentication process

  • Access token endpoint: The URL used to exchange an authorization code for access tokens

  • User info endpoint: The URL that retrieves details about the authenticated user

  • Redirect URL: The destination where users are sent after completing authentication

  • JWKs URI: The URL where the authorization server’s public keys are stored for verifying tokens

  • Scopes: Permissions that define what data and actions the application can access on behalf of the user

You will define these in the .env file. Some values for the variables are already included in the starter template, as are common for everyone. To get the ones that are not provided, navigate to Applications > OIDC default application on your Descope console. Scroll down to the SP Configuration section, copy the value of the Client ID, and assign it to the OAUTH_CLIENT_ID variable in the .env file:

Fig: Obtaining the client ID
Fig: Obtaining the client ID

Make sure you also replace the placeholder inside the OAUTH_JWKS_URI's value with the same client ID.

The OAUTH_SCOPES variable in the .env file specifies the scopes your application will request from Descope. However, Descope does not include the descope.claims (user’s roles, permissions, and tenants) and descope.custom_claims (user’s custom claims) scopes in its response by default unless explicitly configured.

To enable these scopes, go to the OIDC default application details page. In the IDP Configuration section, add descope.claims and descope.custom_claims under Supported Claims. Make sure to save the changes:

Fig: Defining additional claims
Fig: Defining additional claims

To obtain the value for the OAUTH_CLIENT_SECRET variable, navigate to M2M > + Access Key on your Descope console. On the Generate Access Key page, provide a name for your key and select Generate Key. Copy the value of the generated key and assign it to the OAUTH_CLIENT_SECRET variable in the .env file:

Fig: Creating the client secret
Fig: Creating the client secret

You now have all the required values for implementing the authentication logic. Open the app/core/auth.py file and add the following method to the Auth class to initialize the OAuth client with the necessary settings:

# Initialize the OAuth client with settings
def init_oauth(self, settings):
   self.settings = settings
   self.oauth.register(
    name=self.settings.OAUTH_CLIENT_NAME,
    client_id=self.settings.OAUTH_CLIENT_ID,
    client_secret=self.settings.OAUTH_CLIENT_SECRET,
    authorize_url=self.settings.OAUTH_AUTHORIZE_URL,
    access_token_url=self.settings.OAUTH_ACCESS_TOKEN_URL,
    redirect_uri=self.settings.OAUTH_REDIRECT_URI,
    jwks_uri=self.settings.OAUTH_JWKS_URI,
    userinfo_endpoint=self.settings.OAUTH_USERINFO_ENDPOINT,
    client_kwargs={'scope': self.settings.OAUTH_SCOPES},
   )

Add the following methods to the same class. These methods will redirect the user to the authorization endpoint set up for the OAuth client and exchange the returned authorization code for an access token:

# Redirect to authorization endpoint
async def authorize_redirect(self, request: Request, redirect_uri: str):
   return await getattr(self.oauth, self.settings.OAUTH_CLIENT_NAME).authorize_redirect(request, redirect_uri)

# Exchange authorization code for access token   
async def authorize_access_token(self, request: Request):
   try:
    return await getattr(self.oauth, self.settings.OAUTH_CLIENT_NAME).authorize_access_token(request)
   except OAuthError as error:
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail=f"OAuth error: {error.error}"
    )

Add the following method to the same class to retrieve the currently authenticated user from the session:

# Get the current authenticated user name
   def get_current_user(self, request: Request) -> Optional[str]:
    user = request.session.get("user")
    if user:
        return user['email']

This method helps check if a request is authenticated when accessing API routes. If the user is not authenticated, you can take appropriate action, such as redirecting them to the login page.

You’ll also need a method to protect Gradio apps by ensuring the user is authenticated and has the necessary roles. Add the following method to the Auth class:

# Authenticate user and authorize based on path and roles
# To protect Gradio app routes
def authenticate_and_authorize(self, request: Request) -> Optional[str]:
   path = request.url.path
   user = request.session.get("user", {})
   tenants = user.get('tenants', [])
   roles = set()

   if isinstance(tenants, dict):
    for data in tenants.values():
        roles.update(data.get('roles', []))

   # Avoid blocking the gradio queue requests
   if '/gradio_api/queue' in path and user:
    return user['email']

   if user and self.settings.ADMIN_ROLE in roles and path == self.settings.ADMIN_DASHBOARD_PATH:
    print('pass. returning', user['email'])
    return user['email']
   elif user and path == self.settings.USER_DASHBOARD_PATH:
    return user['email']
   else:
    return None

Add the following method to the Auth class to determine which Gradio app an authenticated user should see based on their roles:

# Determine the Gradio app to show the user based on their roles
def get_user_redirect_path(self, request: Request) -> str:
   user = request.session.get("user", {})
   tenants = user.get('tenants', [])
   roles = set()

   if isinstance(tenants, dict):
    for data in tenants.values():
        roles.update(data.get('roles', []))
   else:
    print('You have not set up tenants in your Descope account. You can only access the user dashboard since the roles needed to access the admin dashboard are set up via the tenant.')
 
   if not user:
    return self.settings.LOGIN_PAGE_PATH
 
   if self.settings.ADMIN_ROLE in roles:
    return self.settings.ADMIN_DASHBOARD_PATH
   else:
    return self.settings.USER_DASHBOARD_PATH

To ensure the OAuth client is correctly initialized when the application starts, open the app/main.py file and add the following code immediately after the middleware configuration:

# Initialize OAuth client
auth.init_oauth(settings)

Set up all the auth routes by adding the code to the app/api/routes/auth_router.py file:

# Redirect to OAuth login provider
@router.get('/login')
async def login(request: Request):
   redirect_uri = request.url_for('auth_callback')
   return await auth.authorize_redirect(request, redirect_uri)

# Handle OAuth callback after successful login
@router.get("/auth/callback", name="auth_callback")
async def auth_callback(request: Request):
   try:
    token = await auth.authorize_access_token(request)
    user = token.get("userinfo")
    request.session["user"] = dict(user)
    return RedirectResponse(url="/", status_code=HTTP_302_FOUND)
   except HTTPException as e:
    # Log the error
    print(f"Authentication error: {e.detail}")
    return RedirectResponse(url="/auth/error", status_code=HTTP_302_FOUND)

# Log out the current user   
@router.get('/logout')
async def logout(request: Request):
   auth.logout(request)
   return RedirectResponse(url="/", status_code=HTTP_302_FOUND)

# Authentication error page
@router.get("/error")
async def auth_error():
   return {"error": "Authentication failed. Please try again."}

This code defines routes that allow the user to log in, handle the OAuth callback, and log out of the application.

Set up the dashboard routes that will display the appropriate dashboards to the user based on their roles by replacing the existing route in the app/api/routes/dashboard_router.py file with the following:

# Main entry point, redirects based on user role
@router.get("/")
def index(request: Request):
   user = auth.get_current_user(request)
   if user:
    redirect_path = auth.get_user_redirect_path(request)
    return RedirectResponse(url=redirect_path, status_code=HTTP_302_FOUND)
   else:
    return RedirectResponse(url=auth.settings.LOGIN_PAGE_PATH, status_code=HTTP_302_FOUND)

# Routing endpoint for Gradio dashboards
@router.get("/gradio")
async def route_dashboard(request: Request):
   redirect_path = auth.get_user_redirect_path(request)
   return RedirectResponse(url=redirect_path, status_code=HTTP_302_FOUND)

Lastly, protect the Gradio dashboards by replacing the mount_admin_dashboard and mount_user_dashboard methods in the app/ui/gradio_mount.py file with the following:

# Mount the admin dashboard with authorization   
def mount_admin_dashboard(self):
   with gr.Blocks() as admin_dashboard_wrapper:
    admin_dashboard.main.render()
 
   self.app = gr.mount_gradio_app(
    self.app,
    admin_dashboard_wrapper,
    path=self.settings.ADMIN_DASHBOARD_PATH,
    auth_dependency=self.auth.authenticate_and_authorize
   )

# Mount the user dashboard with authorization
def mount_user_dashboard(self):
   with gr.Blocks() as user_dashboard_wrapper:
    user_dashboard.main.render()
 
   self.app = gr.mount_gradio_app(
    self.app,
    user_dashboard_wrapper,
    path=self.settings.USER_DASHBOARD_PATH,
    auth_dependency=self.auth.authenticate_and_authorize
   )

This code adds the auth_dependency parameter before mounting these apps, which runs before any Gradio-related route in your FastAPI app. This ensures that proper authentication and authorization checks are performed before displaying the dashboards.

You can now log in to the application using either magic links or social login.

Implementing SSO with OIDC using Descope

OIDC is an identity layer built on top of the OAuth 2.0 framework. It allows applications to authenticate users securely by delegating authentication to a trusted IdP, such as Okta. OIDC simplifies SSO by enabling users to log in once and access multiple applications without reentering credentials.

To implement SSO, you can configure Okta as the IdP and Descope as the authentication service. Start by launching your Okta admin dashboard and navigating to Applications > Applications > Browse App Catalog:

Fig: Okta developer dashboard
Fig: Okta developer dashboard

On the Browse App Catalog page, search for descope, and select Descope from the search results:

Fig: Searching for the Descope app
Fig: Searching for the Descope app

Select + Add Integration from the Descope app details page:

Fig: Adding the Descope app
Fig: Adding the Descope app

On the General Settings tab, leave everything as is and click Next:

Fig: General settings tab
Fig: General settings tab

On the Sign-On options tab, select OpenID Connect as the sign-on method:

Fig: Selecting the sign-on method
Fig: Selecting the sign-on method

Scroll down to the Advanced Sign-on Settings section, and in the Callback URL field, provide the value https://api.descope.com/v1/oauth/callback. This defines the URL where the user will be redirected after getting successfully authenticated by Okta. Save the changes by clicking Done at the bottom of the page:

Fig: Providing the OIDC SSO callback URL
Fig: Providing the OIDC SSO callback URL

You also need to assign users to the Descope app you just configured. This will ensure that only authorized users can authenticate via the app. To do this, go to the app’s details page and the Assignments tab and select Assign > Assign to Groups:

Fig: Assigning the app to groups
Fig: Assigning the app to groups

On the Assign Descope to Groups page, select the Assign button beside the Everyone group to allow all users in your organization to authenticate via the app, and select Done to save the changes.

Fig: Assigning the Descope app to everyone
Fig: Assigning the Descope app to everyone

Select the Sign On tab and take note of your OIDC client ID and secret:

Fig: Obtaining client ID and secret
Fig: Obtaining client ID and secret

Go to the Descope console to create a tenant that you will use with the Descope app you just configured in Okta. On your Descope console, navigate to Tenants > + Tenant, provide the tenant details on the Create Tenant form, and select Create:

Fig: Creating a tenant
Fig: Creating a tenant

Open the details page of the Tenant you just created, add your email domain under the Tenant Settings > Details section in the Email domain field, and save the changes:

Fig: Adding the tenant email domain
Fig: Adding the tenant email domain

Select Authentication Methods > SSO from the tenant details page sidebar, and under Authentication Protocol, select OIDC:

Fig: Selecting authentication protocol
Fig: Selecting authentication protocol

Under Tenant Details > SSO Domains, provide your email domain. This will help to determine which SSO configuration to load once a user chooses to authenticate using SSO:

Fig: Setting the SSO domain
Fig: Setting the SSO domain

Scroll down to the SSO configuration > Account Settings section and provide the required values as follows:

  • Provider Name: Okta

  • Client ID: The Client ID you obtained from the Okta dashboard

  • Client Secret: The client secret you obtained from the Okta dashboard

  • Scope: openid profile email

  • Grant Type: Authorization code

To obtain the values needed for the SSO configuration > Connection Settings section, you’ll need the OAuth endpoints from the Okta “well-known” configuration. Navigate to https://<YOUR-OKTA-INSTANCE>.okta.com/.well-known/openid-configuration on your browser, and take note of the issuer and authorization, token, user info, and JWKs endpoints.

Make sure to replace <YOUR-OKTA-INSTANCE> with the correct value, which is your organization’s Okta instance ID.

Fig: Obtaining Okta OAuth endpoints
Fig: Obtaining Okta OAuth endpoints

Head back to the Descope console, provide these values in the respective inputs under SSO configuration > Connection Settings, and save the changes.

Fig: Connection settings
Fig: Connection settings

SSO with Okta as the IdP is now fully configured.

Demonstrating the application

To run the application and confirm that everything is working as expected, use the command fastapi dev app/main.py and navigate to http://localhost:8000 on your browser. You will be redirected to the login page:

Fig: Login page
Fig: Login page

Click the Login button, and you’ll be redirected to Descope’s sign-in page, which is powered by the flow you configured earlier. You can sign in using any of the available methods:

Fig: Flow-powered sign-in page
Fig: Flow-powered sign-in page

After successful authentication, you’ll be redirected to the user dashboard. This is because you don’t have the appropriate roles to access the admin dashboard:

Fig: User dashboard
Fig: User dashboard

If you manually tried to navigate to http://localhost:8000/gradio/admin, you will get an error informing you that you’re not authenticated:

Fig: Error for a user without appropriate roles to access admin dashboard
Fig: Error for a user without appropriate roles to access admin dashboard

To check if a user with the appropriate role can access the admin dashboard, open the users’ page on the Descope console, edit your user to assign them the Tenant Admin role, and save the changes:

Fig: Assigning a role to the user
Fig: Assigning a role to the user

Now, go back to the app, log out, and log in again. You should be redirected to the admin dashboard:

Fig: Admin dashboard
Fig: Admin dashboard

Conclusion

In this guide, you learned how to integrate Descope as an OAuth provider for your Gradio application using OIDC. You also explored how to implement SSO with Okta as the identity provider and Descope as the authentication service. Additionally, you implemented role-based access control to protect Gradio app routes.

Descope is a drag-and-drop customer authentication and identity management platform. Our no- or low-code CIAM solution helps hundreds of organizations easily create and customize their entire user journey using visual workflows—from authentication and authorization to MFA and federated SSO. Customers such as GoFundMe, Navan, You.com, and Branch use Descope to reduce user friction, prevent account takeover, and get a unified view of their customer journey.

To learn more, join our dev community, AuthTown, and explore the Descope documentation.

OSZAR »