Build a private application using SSO

Use your company's identity management system to seamlessly control access to your application

In this guide you will learn how to build a custom application that is integrated with your own Identity Provider (IdP) to authenticate and authorize your users, in a process typically known as Single Sign-On (SSO).

After completing this guide, you'll learn how to:

  • Set up an SSO integration between CARTO and your IdP

  • Modify a regular private application to use your SSO integration

  • Set up Just-in-time provisioning in your CARTO organization

  • Modify your application to use just-in-time provisioning for new users

Scaffolding your application

To give you a quickstart, we're providing a sample private application with the changes covered in the Build a private application guide. We will then add the specific code that we need to use SSO.

git clone https://github.com/CartoDB/carto-for-developers-guides.git
cp -r carto-for-developers-guides/private-app sso-app
cd sso-app
npm install
npm run dev

After that, you should have a project up and running at https://127.0.0.1:5173/

Setting up the SSO integration

Single Sign-On (SSO) integrations are only available for Enterprise Medium plans and above. Please get in touch at support@carto.com if you’re interested in this feature.

A prerequisite for this guide is to set up the Single Sign-On (SSO) integration in your CARTO organization. When this integration is enabled, CARTO delegates authentication to your Identity Provider (IdP), and users who have not logged in are redirected to your IdP's login page.

After the user is authenticated, some of the user information from the IdP is available to CARTO, including the groups that this user is a member of. We synchronize this information as covered in the Groups section in CARTO Workspace.

From now on, we'll be assuming that a valid SSO integration has already been enabled and users are already accessing the CARTO Workspace using your Identity Provider.

Leveraging SSO in your application

To start using SSO in your application to seamlessly authenticate users, we need to add the SSO organization ID (provided by our support team after the SSO integration has been configured) to the .env file:

VITE_ORGANIZATION_ID=XXXXXXXX

Then, we need to add the organization property to the authorizationParams property used to create the Auth0Client:

auth0Client = await createAuth0Client({
  domain: 'auth.carto.com',
  clientId,
  authorizationParams: {
    redirect_uri: window.location.origin,
    audience: 'carto-cloud-native-api',
    organization: import.meta.env.VITE_ORGANIZATION_ID
  }
})

When users now click on the "Login" button, they will be redirected to their corporate login page where they will need to enter their credentials. After authentication is completed, we will be able to call the getTokenSilently method that will return an access token used to authorize the requests to the CARTO API.

🎉 Great! Your CARTO application is now using Single Sign-On to authenticate users.

However, at this point, your app won't be able to authenticate brand-new users that didn't exist previously in CARTO. These users will still be required to access first through the regular CARTO Workspace.

If you need to handle the provisioning of new users, keep advancing on this guide.

Setting up Just-in-time provisioning

When SSO is enabled, you can configure CARTO to create users as soon as they successfully log in for the first time using your IdP login flow, without any additional steps or signup questions. We call this process Just-in-time provisioning. Check our documentation to learn how to configure Just-in-time provisioning in CARTO Workspace.

To continue with this guide and seamlessly onboard new users into your application using just-in-time, enable this setting.

Leveraging Just-in-time provisioning in your application

The first step to onboard new users is to register a new environment variable with your Accounts URL. Your Accounts URL is the base URL where users go for login/signup.

  • For cloud deployments, this is always https://app.carto.com

  • For self-hosted deployments, this is a custom URL, like https://carto-instance.acme.com

Add this line to your .env file with your Accounts URL.

VITE_ACCOUNTS_URL=XXXXXXXXX

Finally, we need to update our authentication flow to accept new users, redirect them to the proper just-in-time provisioning flow, and complete their first login flow after CARTO sends them back to the application. This flow takes a few seconds and no user input is required, making it seamless and easy for new users to authenticate and use your application.

To make things easier, just replace your auth.ts file with the following code:

import { Auth0Client, createAuth0Client } from '@auth0/auth0-spa-js';

let auth0Client: Auth0Client

// This is a new parameter to force new users to log in after CARTO has provisioned them
const FORCE_LOGIN_PARAM = 'forceLogin'

export async function initAuth() {
  let accessToken: string | undefined;

  const clientId = import.meta.env.VITE_CLIENT_ID;
  const organizationId = import.meta.env.VITE_ORGANIZATION_ID;
  const accountsUrl = import.meta.env.VITE_ACCOUNTS_URL;

  auth0Client = await createAuth0Client({
    domain: 'auth.carto.com',
    clientId,
    authorizationParams: {
      redirect_uri: window.location.origin,
      audience: 'carto-cloud-native-api',
      organization: organizationId // your SSO organization ID, as provided by CARTO support
    }
  })
  
  // This is the new authentication flow that manages just-in-time provisioning for new users
  const hasForceLogin = location.search.includes(`${FORCE_LOGIN_PARAM}=`)

  if (location.search.includes('state=') && 
      (location.search.includes('code=') || 
      location.search.includes('error='))) {
    await auth0Client.handleRedirectCallback();
    window.history.replaceState({}, document.title, '/');
  }

  const isAuthenticated = await auth0Client.isAuthenticated();
  const appEl = document.getElementById('app');

  if (hasForceLogin) {
    auth0Client.loginWithRedirect()
  } else if (isAuthenticated) {
    const userProfile = await auth0Client.getUser();
    const hasUserMetadata = userProfile[`http://app.carto.com/user_metadata`];
    if (hasUserMetadata) {
      appEl?.classList.add('isAuthenticated');
      accessToken = await auth0Client.getTokenSilently();

      const profileEl = document.getElementById('profile')!;
      profileEl.innerHTML = `
        <p>${userProfile.name}</p>
        <img width="50px" height="50px" src='${userProfile.picture}' />
      `;
    } else {
      // No organizations associated with the user, we need to redirect to app.carto.com to complete the signup process
      const redirectAccountsUri = `${accountsUrl}sso/${organizationId}`
      const searchParams = new URLSearchParams({
        redirectUri: `${window.location.origin}?${FORCE_LOGIN_PARAM}=true`,
      });
      window.location.href = `${redirectAccountsUri}?${searchParams}`;
    }
  } else {
    appEl?.classList.remove('isAuthenticated');
  }
  
  // Logout
  const logoutButton = document.getElementById('logout');
  logoutButton?.addEventListener('click', (e) => {
    e.preventDefault();
    auth0Client.logout();
  });

  // Login
  const loginButton = document.getElementById('login');
  loginButton?.addEventListener('click', (e) => {
    e.preventDefault();
    auth0Client.loginWithRedirect();
  });
  
  return accessToken
}

Testing our SSO application with a new user

With these changes, our application now displays a login button that redirects the user to your Identity Provider (IdP). If the user already exists in CARTO, they will go straight to your app. If the user is completely new, they will also go straight to your app, but a new CARTO user will be provisioned in the process.

Here's what the experience looks like for a completely new user, after enabling just-in-time provisioning.

🎉 Congratulations! Your CARTO application is now using Single Sign-On to authenticate users and is also able to seamlessly create new users if they access your app for the first time.

What's next?

Now that your application is successfully authenticating users via SSO, let's focus on creating interesting content.

We recommend you to visit CARTO + deck.gl to learn more about the visualizations you can create in deck.gl. There are many examples in our gallery that might be useful to improve your application!

Last updated