Skip to main contentArrow Right

Table of Contents

This tutorial was written by Vivek Maskara, a software development engineer at Remitly. You can check out his website or X account to connect and see more of his work!


Shiny lets you easily build web apps for data science using R and Python without the need for any web development experience. You can deploy these applications on your own web server or use Posit Connect to deploy them directly from your IDE.

Although deploying the applications is straightforward, it’s also vital to set up proper access control for these applications to ensure that only authenticated users can view and interact with the data. Integrating single sign-on (SSO) with Shiny apps can provide a seamless authentication experience for the users. As these apps are often used by different business or enterprise users who might need to log in with their own identity provider (IdP), SSO allows them to use their existing credentials to log in.

Descope offers a no-code Customer Identity Platform (CIAM) that lets you easily define a customized workflow for sign-up, login, multi-factor authentication (MFA), SSO, and Security Assertion Markup Language (SAML).

In this tutorial, you’ll learn how to implement authentication and SSO in a Shiny app.

Prerequisites

Before starting, you need to do the following:

Set up Descope

Before you can integrate Descope authentication into your Shiny application, you need to define an authentication flow using the Descope portal.

Sign up for a free Descope account if you don’t already have one, and follow the Getting Started wizard to create a new flow. In the wizard, first, choose Consumers as the target audience for your app:

Fig: Choose target users
Fig: Choose target users

Next, choose the authentication methods you want to use. In this tutorial, you’ll use Magic Link and SSO as the authentication method. You can change these settings in the future if you like:

Fig: Choose authentication methods
Fig: Choose authentication methods

Skip the next step that prompts you to choose an MFA method by clicking Go ahead without MFA. In the next step, review the selected settings and click Next to generate the authentication flow:

Fig: Review authentication methods
Fig: Review authentication methods

Create a Shiny app

In this tutorial, you’ll create a personal finance application using Shiny and add Descope authentication. The Shiny app will let you upload a CSV file containing month-wise expense data, view a bar chart with monthly expense trends, and also analyze category-wise expenses.

Since the tutorial focuses on adding authentication, you can clone the starter code with the personal finance Shiny app. In the terminal, navigate to the project directory and check out the starter branch.

cd personal-finance-shiny
git checkout starter

Open the application using RStudio and attempt to run the starter app. When you run it for the first time, it will prompt you to install the required packages. After you install the packages and rerun the app, you’ll notice that you’re directly taken to the dashboard page without authentication:

Fig: Personal finance Shiny app
Fig: Personal finance Shiny app

Next, click on the Publish button in RStudio to deploy the app on Posit Connect.

Fig: Publish Shiny app
Fig: Publish Shiny app

Before you can publish the Shiny app, you need to connect your Posit Connect account by clicking Add new account and choosing Posit Connect. Enter the Posit Connect server URL and click Next to connect the account.

Fig: Add Posit Connect account
Fig: Add Posit Connect account

You’ll be redirected to the web browser to confirm the linking between RStudio and Posit Connect. Click Connect to link the account.

Fig: Confirm Posit connect connection with RStudio
Fig: Confirm Posit connect connection with RStudio

After confirming the connection in the browser, navigate back to RStudio and click Connect Account to verify the account.

Fig: Verify Posit connect account linking
Fig: Verify Posit connect account linking

Finally, select the connected account, provide a title for the app, and click Publish to deploy the application.

Fig: Publish Shiny app to Posit Connect
Fig: Publish Shiny app to Posit Connect

The dashboard of the app has the following components:

  • The left section lets you upload a CSV file containing personal expense data. The GitHub repo contains a sample CSV that you could use for testing.

  • When you upload the CSV file, the right section lets you view and analyze the category-wise expenses based on the uploaded data.

Implement vanilla authentication with Descope

You’ve deployed the app to Posit Connect and can now add authentication methods to access it using Descope. In this section, you’ll add auth using magic links and email OTP for simplicity. You can configure and use other authentication methods too but we will focus on magic links and OTP for this tutorial.

Descope offers a WebJS component to integrate its auth flows with HTML-based web apps. You can easily integrate the WebJS component with your Shiny app by adding a custom JavaScript widget.

In your R project, create a www/descope-auth.js file, add the following content to create a container for the Descope element, and set success and failure listeners:

// www/descope-auth.js
Shiny.addCustomMessageHandler('initDescopeAuth', function (config) {
    const wcElement = document.createElement('descope-wc');
    wcElement.setAttribute('project-id', config.projectId);
    wcElement.setAttribute('flow-id', config.flowId);
    wcElement.setAttribute('theme', config.theme);

    wcElement.addEventListener('success', function (e) {
        const user = e.detail.user;
        Shiny.setInputValue(config.successInput, user);
    });

    wcElement.addEventListener('error', function (err) {
        console.error('Authentication error:', err);
        Shiny.setInputValue(config.errorInput, err);
    });

    const container = document.getElementById(config.containerId);
    if (container) {
        container.innerHTML = ''; // Clear any previous instances
        container.appendChild(wcElement);
    }
});

Next, you need to update the UI definition to include the @descope/web-component and descope-auth.js JS scripts. Update the app.R file by replacing it with the following code snippet:

ui <- fluidPage(
  useShinyjs(), # For dynamic UI control
  tags$head(
    tags$script(src = "https://unpkg.com/@descope/web-component@3.32.0/dist/index.js"),
    tags$script(src = "descope-auth.js") # Include custom JavaScript
  ),
  titlePanel("Personal Finance Dashboard with Descope Auth"),
  
  # Descope Authentication UI
  div(id = "authContainer"),
  
  hidden(
    div(
      id = "appContent",
      sidebarLayout(
        sidebarPanel(
          fileInput("file", "Upload Expense Data (CSV)", accept = ".csv"),
          selectInput("category", "Select Category:", choices = NULL),
          actionButton("analyze", "Analyze")
        ),
        mainPanel(
          tabsetPanel(
            tabPanel("Overview", plotOutput("expenseTrend"), tableOutput("summaryTable")),
            tabPanel("Category Analysis", plotOutput("categoryTrend"), tableOutput("categoryTable"))
          )
        )
      )
    )
  )
)

Finally, you need to update the server definition to initialize the Descope web component and implement success and failure callbacks. Add the following code snippet at the start of the server section:

server <- function(input, output, session) {
  # Initialize Descope Auth
  session$sendCustomMessage("initDescopeAuth", list(
    projectId = "<YOUR_DESCOPE_PROJECT_ID>",
    flowId = "sign-up-or-in",
    theme = "light",
    containerId = "authContainer",
    successInput = "user_authenticated",
    errorInput = "auth_error"
  ))
  
  observeEvent(input$user_authenticated, {
    if (!is.null(input$user_authenticated)) {
      showNotification("Login successful!", type = "message")
      show("appContent")
    }
  })
  
  observeEvent(input$auth_error, {
    if (!is.null(input$auth_error)) {
      showNotification("Authentication failed. Please try again.", type = "error")
    }
  })
  ...
  # existing server definition
}

Replace YOUR_DESCOPE_PROJECT_ID with your Descope project ID, save the changes, and republish the app to Posit Connect using RStudio.

Test authentication using magic link

When a user clicks the magic link received in their email for authentication, Descope will validate the auth and redirect the user back to the Shiny application. For redirection to work, you need to configure the auth redirect URL in Descope. Head over to Magic link under Authentication Methods and set the Redirect URL to the published URL of your Shiny app.

Fig: Configure Magic link redirect URL
Fig: Configure Magic link redirect URL

To test the authentication, open the published content under the Content tab on Posit Connect.

Fig: Open published content from Posit Connect
Fig: Open published content from Posit Connect

When you run the app, you’ll be redirected to the Descope auth screen. You can test the integration by entering your email ID on this screen and clicking the Continue button to log in with a magic link. You’ll receive an email with a login link, and upon clicking it, you’ll be redirected to the dashboard page of your Shiny app.

Fig: Descope auth screen
Fig: Descope auth screen

Create a new Descope tenant

After integrating vanilla authentication, the next step is to add SSO authentication using Descope. To do this, you first need to create a Tenant. Tenants represent organizations that use your application and make it easier to group and manage users, permissions, and authentication flows within the application. In this tutorial, you’ll create a tenant for an example organization and use it to configure the SSO flow.

Navigate to Tenants and click the + Tenant button to create a new tenant.

Fig: Create a new tenant
Fig: Create a new tenant

Implement SSO with OIDC using Descope

OpenID Connect (OIDC) is an extension of the OAuth 2.0 authorization protocol that provides a standardized way for applications to request and receive identity information about users. Instead of managing user credentials for each application, OIDC SSO lets you delegate the authentication mechanism to a single trusted IdP, reducing the maintenance overhead. Once you log in, the IdP returns standardized JSON web tokens (JWTs) on login-containing information about the user’s identity. In this section, you’ll learn how to set up Descope as an OIDC IdP with Posit Connect.

Create a Descope OIDC application in a separate project

The Descope OIDC application acts as a federated IdP for the users. Based on your needs, Descope can act as a Service Provider (SP) or an IdP. An SP is an entity that initiates an authentication request to an IdP, which verifies the user’s identity. After authentication, the SP processes the response from the IdP to determine whether the user should be granted access to the application. In the previous section, you configured the Descope project as an SP entity for auth and SSO.

You’ll now create an OIDC application within Descope to turn it into an IdP, and subsequently, you’ll also integrate it with your authentication flow. Since you’re using Descope for both SP and IdP, to avoid cyclic dependency, both these entities should be configured in separate Descope projects.

First, use the Descope portal to create a new project to host the OIDC application. This tutorial sets the project name as descope-shiny-auth-sso.

Fig: Create new Descope project
Fig: Create new Descope project

You also need to create an OIDC application under the newly created project. Navigate to Applications and click the + Application button to register Generic OIDC Application.

Fig: Create a new OIDC application under the new project
Fig: Create a new OIDC application under the new project

Click Create to create the OIDC application and note the Descope OIDC endpoint configuration, as you’ll need it while configuring the SSO tenant in the original project.

To configure OAuth with Posit Connect, you’ll also need to create a new client secret. Navigate to the Access Keys page and click +Access key to create a new access key.

Fig: Create a new access key
Fig: Create a new access key

Configure Descope tenant to use OIDC SSO

To integrate the OIDC application with the authentication flow, you need to configure SSO under the example.com tenant you created under the original Descope SP project. A tenant lets you configure an authentication method using an OIDC or SAML application. This section discusses how to configure SSO using an OIDC application, and in a later section, you’ll learn how to configure SSO using SAML.

Switch back to the Descope SP project and navigate to Tenants, and select the example.com tenant you created earlier. Choose Authentication Methods and select OIDC as the SSO authentication protocol. Make the following changes to configure SSO:

  • Under Tenant Details, add example.com to the SSO domains.

  • Update the SSO configuration by adding the following values:

    • Provider name: Add a value for the provider. For example, the tutorial uses Descope as the provider name.

    • Client ID: Use the Descope project ID for the descope-shiny-auth-sso project.

    • Client Secret: Use the access key that you created above.

    • Scopes: It sets the permissions for the access level that will be granted to the authenticated user. Add openid, profile, and email as the scopes.

    • Issuer: Use the Issuer value from the OIDC application you created in the previous step.

    • Authorization Endpoint: Use the Authorization URL value from the OIDC application.

    • Token Endpoint: Use the Access Token URL value from the OIDC application.

    • User Info Endpoint: Use the Access Token URL from the OIDC application by replacing /token with /userinfo.

  • Finally, Save the changes for them to take effect.

Configure SSO redirect URL

You also need to configure the Posit Connect application URL as an SSO redirect URL. Since the Shiny app is already configured to use Descope, it doesn’t require any new changes. Grab the application URL, navigate to SSO under Authentication methods, and set it as the Redirect URL.

Fig: Set SSO redirect URL
Fig: Set SSO redirect URL

Note that the SSO redirect URL needs to be configured in the Descope SP project.

Test the OIDC SSO integration

Finally, to test the integration, open the application in the browser, enter your email ID, and click on Continue with SSO to initiate the SSO flow. Note that the email ID should use example.com for SSO to work since you didn’t configure any other domains in Descope.

Fig: SSO login
Fig: SSO login

You’ll be redirected to the IdP login screen, which, in this case, is hosted by the descope-shiny-auth-sso Descope project. You can verify this by comparing the project ID in the address bar with the Descope project ID.

Fig: Descope IdP login
Fig: Descope IdP login

For testing, complete the login using one of the social login options or the magic link, and upon authentication, you’ll be redirected back to the Shiny dashboard.

Fig: Shiny dashboard after login
Fig: Shiny dashboard after login

Implement SAML SSO with Descope

SAML SSO is an XML-based open authentication standard that lets users log in to multiple web applications using a single set of credentials. SAML is a mature identity federation standard and is widely supported by legacy or internal enterprise applications. It serves a similar purpose to OIDC, but instead of JSON, it uses XML during authentication or authorization requests between the IdP and the SPs.

In this section, you’ll learn how to configure SAML SSO with Descope using a mock SAML application and configuring the tenant to use SAML SSO.

Configure mock SAML tenant for testing

As you learned in the OIDC SSO section, Descope can act as both SP and IdP as needed, but this tutorial uses Descope as an SP and Mock SAML as an IdP. Mock SAML is a SAML 2.0 IdP that can test SAML SSO integrations quickly. If you don’t have a SAML SP set up, you can use this tool to test your Descope SAML integration.

Navigate to Tenants and select the example.com tenant you created earlier. Choose Authentication Methods and change the SSO authentication protocol from OIDC to SAML. Make the following changes to configure SAML SSO:

  • Under Tenant Details, add example.com to the SSO domains.

  • Update the SSO configuration, select the Retrieve the connection details dynamically option, and set the Metadata URL for IdP to the following:

https://mocksaml.com/api/saml/metadata

Note: If you have a SAML SP setup, you can set the Descope Metadata (URL) of the SAML application instead of a Mock SAML URL.

Test the SAML SSO integration

To test the integration, open the application in the browser, enter your email ID, and click on Continue with SSO to initiate the SSO flow. Note that the email ID should use example.com for SSO to work since you didn’t configure any other domains in Descope. If you used a mock URL, you’ll be redirected to a MockSAML login page where you can confirm your email ID and click Sign in.

Fig: SAML SSO login
Fig: SAML SSO login

Note: If you configured a Descope SAML application with SP details, Descope will directly handle the authentication.

After logging in, you’ll be redirected to the dashboard page.

Fig: Shiny dashboard after SAML login
Fig: Shiny dashboard after SAML login

Access users from Shiny

So you’ve now added authentication to the Shiny app using OIDC and SAML, but the Shiny application doesn’t indicate that the user is logged in. This section discusses how to access the logged-in user metadata and display a welcome message in the UI.

You’ll add a UI element to the sidebar and update the server definition to set the welcome message when the user is authenticated. First, add a welcomeText element to the sidebar by updating the sidebarPanel to the following:

sidebarPanel(
  # Add a welcome text element
  h3(textOutput("welcomeText")),
  br(),
  
  # Existing inputs
  fileInput("file", "Upload Expense Data (CSV)", accept = ".csv"),
  selectInput("category", "Select Category:", choices = NULL),
  actionButton("analyze", "Analyze"),
)

Update the user_authenticated observer under the server definition to set the welcome message:

observeEvent(input$user_authenticated, {
  req(input$user_authenticated)
  
  user_info <- input$user_authenticated
  
  # use "name", else fallback to "email", else "User"
  user_name <- user_info$name
  if (is.null(user_name)) user_name <- user_info$email
  if (is.null(user_name)) user_name <- "User"
  
  output$welcomeText <- renderText({
    paste("Welcome,", user_name)
  })
  
  showNotification("Login successful!", type = "message")
  show("appContent")
})

Notice that the above code snippet tries to retrieve the user’s name from the user_info object and falls back to email if name is not present.

Save and republish the changes to test them. Once you log in to the dashboard, a welcome message will be displayed.

Fig: Welcome message based on Descope user info
Fig: Welcome message based on Descope user info

Log out a user

Your users also need logout functionality to enable them to end their sessions. To implement logout, first, add a logout button to the sidebar by updating the sidebarPanel as follows:

sidebarPanel(
  h3(textOutput("welcomeText")),
  br(),
  
  # Existing inputs
  fileInput("file", "Upload Expense Data (CSV)", accept = ".csv"),
  selectInput("category", "Select Category:", choices = NULL),
  actionButton("analyze", "Analyze"),
  
  # Logout button
  actionButton("logout", "Logout", icon = icon("sign-out-alt"))
)

Next, add a descopeLogout message handler in the descope-auth.js file that will be invoked when the logout button is clicked:

Shiny.addCustomMessageHandler('descopeLogout', function () {
    const wcElement = document.querySelector('descope-wc');
    if (wcElement) {
        wcElement.logout();
    }
});

Finally, add a logout observer under the server definition to invoke the descopeLogout and initDescopeAuth message handlers.

observeEvent(input$logout, {
  hide("appContent")  # Hide main content
  session$sendCustomMessage("descopeLogout", list())
  
  session$sendCustomMessage("initDescopeAuth", list(
    projectId = "P2pBl7sYVGg1RWtv4jC7zfO9TnUN",
    flowId = "sign-up-or-in",
    theme = "light",
    containerId = "authContainer",
    successInput = "user_authenticated",
    errorInput = "auth_error"
  ))
})

Save and republish the changes to test them. Once you log in to the dashboard, you’ll notice a Logout button being displayed next to the Analyze button.

Fig: Logout button
Fig: Logout button

When you click the Logout button, you’ll be logged out of the application and redirected to the Descope login page.

Make Shiny authentication simpler with Descope

Shiny makes it easier for data engineers and scientists to share their work with others as web applications without any HTML or JavaScript experience. Posit Connect is a publishing platform that lets you deploy Shiny apps directly from RStudio with a click of a button, eliminating the need to provision and maintain a web server.

In this article, you learned how to implement authentication and authorization in a Shiny app using Descope. You can find this tutorial’s complete source code on GitHub.

Descope is a drag-and-drop customer authentication and identity management platform. Our low- or no-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 like Navan, GoFundMe, You.com, and Branch Insurance use Descope to reduce user friction, prevent account takeover, and get a unified view of their customer journey.

To learn more, book a demo with our auth experts or join our dev community, AuthTown.