# SSO via JWT Tutorial

# Introduction

Single Sign-On (SSO) is a mechanism that allows a system to authenticate users and subsequently tell Sisense that the user has been authenticated. The user is then allowed to access Sisense without being prompted to enter separate login credentials.

This tutorial will walk you through the steps required to implement JWT-based Single Sign On with Sisense, and demonstrate this process via a demo "parent application". Before you begin, check out our SSO Documentation for general information about SSO and JWT.

This tutorial includes the following required steps for setting up JWT-based SSO:

# Prerequisites

  1. This tutorial covers the development of a .NET-based SSO handler written using C#. Prefer an IDE capable of editing C#.
  2. Download the JWT Starter Kit (opens new window)
  3. Setup the demo application according to the instructions included in the SSO-JWT Starter Kit located in the readme.md file.

# Developing an SSO Handler

There are several steps to developing an SSO handler.

  1. In this tutorial, the first step is to extract the cookie of an authenticated user so a token can be created later on.
  2. The next step is to generate an object that contains the required information for a JWT token.
  3. After this object has been created, the third step is to configure SSO in the Sisense Admin Console and retrieve a shared secret key that will be used in the next step to encrypt the JWT token.
  4. The final step in developing an SSO handler is to redirect the request to the desired destination with the encrypted token and return_to values.

# Goal

In this step, you will identify the user currently logged in the parent application. In this example, the username of the logged in user is stored in a browser cookie on the parent application’s domain, however in other cases, the cookie may contain an encoded token that your web server can decipher, or it may be an entirely different way of implementation. As implementations vary between applications, you can find the appropriate method of identifying the current user by looking at your application’s code or documentation.

# Actions

Add the following function to your SSO Handler file, SSOHandler.ashx

// This method returns the username from the login cookie, or null if no user is logged in.
public string ExtractUser(HttpContext context)
{
   // Get the correct cookie from the request
   var Cookie = context.Request.Cookies["dummyUser"];
 
   // Return the cookie's value if it exists
   if ((Cookie != null) && (Cookie.Value != null))
      return Cookie.Value;
 
   // Return null otherwise
   return null;
}

# Testing

First, ensure you have a cookie by going to the Login page of the sample host application, and enter a username and click Log in.

You can then test your code by adding the following lines to the main ProcessRequest function and accessing your handler:

string username = ExtractUser(context);
context.Response.Write(username);

You should see the following response in your browser:

Note: Don’t forget to remove/comment out the above lines after testing!

# Step 2: Generating a JWT - Part 1: The Object

# Goal

To generate the JWT (JSON Web Token), you will need to create an object containing all the required fields as documented here. In a later step, this object will be encoded using your unique secret key to create the actual token.

# Actions

To generate this object, add the following function to your SSO handler:

// This function generates the JWT object
public System.Collections.Generic.Dictionary<string, object> GenerateJWTPayload(string username)
{
    TimeSpan timeSinceEpoch = (DateTime.UtcNow - new DateTime(1970, 1, 1));
    int secondsSinceEpoch = (int)timeSinceEpoch.TotalSeconds;
 
    var payload = new System.Collections.Generic.Dictionary<string, object>() {
        { "iat", secondsSinceEpoch },
        { "sub", username },
        { "jti", Guid.NewGuid() },
        { "tenantId", tenantId } // Required for organization tenants.
    };
 
    return payload;
}

# Testing

Similarly to the previous step, add the following lines to your ProcessRequest function:

// Generate JWT object
var payload = GenerateJWTPayload(username);
var json = new JavaScriptSerializer().Serialize(payload);
context.Response.Write(json);

Now, when you navigate to your SSOHandler.ashx you should see a JSON object similar to the object displayed below:

# Step 3: Configure SSO in Sisense and Retrieve Secret Key

# Goal

In this step, you will turn on SSO in the Sisense Admin panel, configure it, and retrieve the secret key.

# Actions

First, log in to Sisense as an Administrator and open the Admin page. Select the Single Sign On tab:

Then, turn on SSO, paste the SSO handler’s URL in the Remote Login URL field, and copy the contents of the Shared Secret field:

# Testing

There are no tests required for this step.

# Step 4: Generating a JWT - Part 2: Encoding

# Goal

In this step, you will use the secret key from the Sisense Admin SSO page to encode the object created in Step #2.

# Actions

Copy this function to your SSO handler:

// This function encodes a JWT object
public string EncodeJWT(System.Collections.Generic.Dictionary<string, object> payload)
{
    string secret = "<insert shared secret here>"; // TODO: replace with your shared secret
    string token = JWT.JsonWebToken.Encode(payload, secret, JWT.JwtHashAlgorithm.HS256);
    return token;
}

Replace <insert shared secret here> with your shared secret copied in the previous step.

# Testing

Similarly to Steps #1 & #2, add the following lines to your ProcessRequest function:

// Encode JWT object into a token
string token = EncodeJWT(payload);
context.Response.Write(token);

Now, when you navigate to your SSOHandler.ashx you should see an encoded string similar to the one displayed below:

# Additional Information

Note the HS256 algorithm used in the above code sample. Sisense uses this shared-key encryption scheme. When you install Sisense, a private key and a shared key are generated. The shared key can be used to encode a message, which can only be decoded by using the private key which is known only to your Sisense instance. This ensures the user information passed between the SSO handler and Sisense remains secure, and prevents a 3rd-party from mimicking a generic JWT and accessing your Sisense deployment.

Hence, this process will only work when all the below conditions are met:

  • You used the correct shared key provided by Sisense from the current deployment. If you had one, but re-installed Sisense or are deploying on new servers, you cannot reuse a previous key!
  • You use the same encryption algorithm (currently HS256) as Sisense does.

# Step 5: Joining it All Together

# Goal

In this step, you will finalize your SSO Handler utilizing the results of all the previous steps.

# Actions

Put the following code in your ProcessRequest function. It will extract the username from the cookie using the code from Step #1, generate the JWT object, encode it using the secret key into a token, and redirect the request to the desired destination with the token and return_to values as parameters.

public void ProcessRequest(HttpContext context)
{
    // Get currently logged in user
    string username = ExtractUser(context);
 
    // If user is not logged in, redirect to main login page
    if(username == null)
    {
        context.Response.Redirect("./default.aspx");
    }
 
    // Generate JWT object
    var payload = GenerateJWTPayload(username);
 
    // Encode JWT object into a token
    string token = EncodeJWT(payload);
 
    // This is the Sisense URL which can handle (decode and process) the JWT token
    string redirectUrl = "http://reporting.mytestapp.com:8081/jwt?jwt=" + token;
 
    // Which URL the user was initially trying to open
    string returnTo = context.Request.QueryString["return_to"];
    if (returnTo != null)
    {
        redirectUrl += "&return_to=" + HttpUtility.UrlEncode(returnTo);
    }
 
    // Perform the redirect
    context.Response.Redirect(redirectUrl);
}

# Testing

At this stage, when navigating to your SSOHandler.ashx you should be redirected to the Login page (if no cookie exists) or to the reporting page.

# Additional Information

Note the return_to query string parameter. When a user tries accessing a specific Sisense URL (for example, a specific dashboard) and is redirected to the SSO handler for login, the original URL the user tried to access is being passed to the handler. This block of code then attaches it to the redirect URL, which in turn tells Sisense to open that view once the JWT is processed and the user is authenticated. This mechanism ensures a smoother user experience.

# Implementing Log Out

Now that your users can log in, you need to implement a flow for logging out.

# Background

Your parent application has a Logout button that deletes the user cookie and redirects the user to the Login page. You would like the Sisense cookie to be deleted as well, to ensure the user is logged out from Sisense so that when a new user logs in they go through the SSO process again and are logged in to Sisense correctly.

There are three main ways to log out from Sisense:

  • Navigating the iFrame to the /api/auth/logout endpoint
  • Sending a JavaScript postMessage to an Add-on that logs a user out
  • Using the advanced log-out API

The first method is easiest to implement, but is limited - it requires an iFrame, whether visible or hidden. This is because simply sending an AJAX request from the host application to this endpoint will pass on the host application’s cookies, and not the Sisense cookies, thus not performing the desired log-out operation.

The second method has the same limitations and is slightly more complex to implement, but it is considered safer and quicker. The actual logout action is performed by Sisense in this case.

The third is the most complex, but also the most robust approach as it can be called from any state of your host application regardless of whether the Sisense iFrame currently exists or not.

# Step 6: Implementing Log-out

# Goal

For this tutorial, you will use the first method for simplicity and brevity.

# Actions

Add the following code to your host application’s logout function in the reporting.aspx page:

$('#logout').click(() => {
    // Log out by navigating the iFrame to the Logout API
    $('#frame1').attr('src', 'http://reporting.mytestapp.com:8081/api/auth/logout');
 
 
    // Remove the user's cookie
    Cookies.remove('dummyUser');
 
 
    // Navigate to the main page
    window.location.href = '/default.aspx';
});

When the logout button is clicked, this function will perform three operations:

  1. Log the user out of Sisense
  2. Log the user out of the host application (in this case by removing the user’s cookie)
  3. Return the user back to the main Login page

# Additional Information

This of course is a simplified approach. In a real world use case, you would likely need the following amendments:

  • Ensure the iFrame exists, and create a hidden one if it doesn’t, so that the Sisense log-out operation can be performed regardless of which page the user is on
  • Have a more complex logout process from your own host application, and likely have this logic implemented in some centralized service and used across multiple views

To read more about using postMessage and the logout API, please visit these pages:

# Testing the SSO Flow

The final step is to test your SSO flow.

Perform the following steps:

  1. In an "incognito" window, browse to http://reporting.mytestapp.com:8081 > You should see the Sisense Login page as you are not logged in.
  2. Browse to http://main.mytestapp.com:8083. You should reach the host application’s login page.
  3. Enter a valid email and click Log in. You should be directed to the reporting page, and Sisense should open in the iframe with the username you have entered.
  4. Open a new tab and navigate to http://reporting.mytestapp.com:8081. You should now be logged in and reach the homepage.
  5. Return to the other tab and click the logout button. You should be redirected to the host application’s login page again.
  6. In the second tab, refresh. You should again reach the Sisense login page, confirming you have been logged out of Sisense.

# Summary

Congratulations! You have successfully implemented basic Single Sign On with Sisense.

At this stage you should comfortably understand the standard SSO flow when working with Sisense. This tutorial covers one specific scenario, when Sisense is embedded with an iFrame into a rather simple host application. However, your project might differ in many ways, such as:

  • A different method of login/authentication in the host application
  • A different method of embedding such as SisenseJS
  • No embedding at all
  • Other server-side languages (this demo uses ASP.NET and C#)

If you have any further questions, you can ask on our customer forums (opens new window) or contact Sisense Support.