Articles in this section
Category / Section

Embedding Bold BI Dashboard in ASP .NET Core Applications with Embed Token Authentication

Published:

This article demonstrates how to embed a Bold BI dashboard in an ASP .NET Core 8.0 application using embed token authentication. The sample shows how to securely render and list dashboards with minimal API usage, while supporting row-level security, group-based authorization, and anonymous user embedding using embed token authentication.

Prerequisites

How to run the sample

  • Please get the ASP.NET core sample from GitHub.

  • Ensure that you have enabled embedded authentication on the Embed Settings page. If it is not currently enabled, refer to the following image or the detailed instructions to enable it.
    Embed Settings

  • To download the embedConfig.json file, please follow this link for reference. Additionally, you can refer to the following image for visual guidance.
    Download Embed Settings
    image.png

  • Copy the downloaded embedConfig.json file and paste it into the designated location within the application. Please ensure that you have placed it in the correct location, as shown in the following image.
    image.png

  • Open the ASP .NET Core sample in Visual Studio Code.

  • Open the terminal in Visual Studio Code and execute the command dotnet restore to restore the required dependencies.

  • Build your .NET project by executing the dotnet build command in the terminal.

  • To run the application, use the command dotnet run in the terminal. After the application has started, it will display a URL in the command line interface, typically something like (e.g., http://localhost:5000). Copy this URL and paste it into your default web browser.

How the sample works

  • Based on the values provided in the embedConfig.json file, the application obtains a user token and validates it. Then, it retrieves the list of available dashboards from the Bold BI server using a Rest API call.

       public IActionResult Index()
       {
           try
           {
               string basePath = AppDomain.CurrentDomain.BaseDirectory;
               string jsonString = System.IO.File.ReadAllText(Path.Combine(basePath, "embedConfig.json"));
               GlobalAppSettings.EmbedDetails = JsonConvert.DeserializeObject<EmbedDetails>(jsonString);
    
               // Pass specific properties to the view using ViewBag
               ViewBag.DashboardId = GlobalAppSettings.EmbedDetails.DashboardId;
               ViewBag.ServerUrl = GlobalAppSettings.EmbedDetails.ServerUrl;
               ViewBag.EmbedType = GlobalAppSettings.EmbedDetails.EmbedType;
               ViewBag.Environment = GlobalAppSettings.EmbedDetails.Environment;
               ViewBag.SiteIdentifier = GlobalAppSettings.EmbedDetails.SiteIdentifier;
    
               return View();
           }
           catch
           {
               return View("EmbedConfigErrorLog");
           }
       } 
    
  • In HomeController.cs, the GetDashboards() action uses the GetToken method to authenticate and fetch the dashboard list, which is used to dynamically populate the DOM in Index.html.

       [HttpGet]
       [Route("GetDashboards")]
       public string GetDashboards()
       {
           var token = GetToken();
    
           using (var client = new HttpClient())
           {
               client.BaseAddress = new Uri(GlobalAppSettings.EmbedDetails.ServerUrl);
               client.DefaultRequestHeaders.Accept.Clear();
               client.DefaultRequestHeaders.Add("Authorization", token.TokenType + " " + token.AccessToken);
               var result = client.GetAsync(GlobalAppSettings.EmbedDetails.ServerUrl + "/api/" + GlobalAppSettings.EmbedDetails.SiteIdentifier + "/v5.0/dashboards").Result;
               string resultContent = result.Content.ReadAsStringAsync().Result;
               var dashboardsJson = JObject.Parse(resultContent)["Data"].ToString();
               return dashboardsJson;
           }
       }
    
       public Token GetToken()
       {
           using (var client = new HttpClient())
           {
               client.BaseAddress = new Uri(GlobalAppSettings.EmbedDetails.ServerUrl);
               client.DefaultRequestHeaders.Accept.Clear();
    
               var content = new FormUrlEncodedContent(new[]
               {
                   new KeyValuePair<string, string>("grant_type", "embed_secret"),
                   new KeyValuePair<string, string>("Username", GlobalAppSettings.EmbedDetails.UserEmail),
                   new KeyValuePair<string, string>("embed_secret", GlobalAppSettings.EmbedDetails.EmbedSecret)
               });
               var result = client.PostAsync(GlobalAppSettings.EmbedDetails.ServerUrl + "/api/" + GlobalAppSettings.EmbedDetails.SiteIdentifier + "/token", content).Result;
               string resultContent = result.Content.ReadAsStringAsync().Result;
               var response = JsonConvert.DeserializeObject<Token>(resultContent);
               return response;
           }
       } 
    
  • After receiving the dashboard list, an embed query string is manually generated using the first dashboard ID, access mode, and expiration time

        var token = ""
        var isMultiTab = false
        let tokenExpiry = null;
        function Init() {
            var http = new XMLHttpRequest();
            http.open("GET", getDashboardsUrl, true);
            http.responseType = 'json';
            http.setRequestHeader("Content-type", "application/json");
            http.onreadystatechange = function () {
                if (http.readyState == 4 && http.status == 200) {
                    ListDashboards.call(this, typeof http.response == "object" ? http.response : JSON.parse(http.response));
                }
                else if (http.readyState == 4 && http.status == 404) {
                    console.log("Server not found");
                }
                else if (http.readyState == 4) {
                    console.log(http.statusText);
                }
            };
        
            http.send();
        };
        
        function ListDashboards(data) {
            if (typeof (data) != "undefined" && data != null) {
                getDashboardAccessToken(data[0].Id, 'view', '100000');
                data.forEach(function (element) {
                    var divTag = document.createElement("div");
                    divTag.innerHTML = element.Name;
                    divTag.className = "dashboard-item";
                    divTag.setAttribute("onclick", "renderDashboard('" + element.Id + "')");
                    divTag.setAttribute("name", element.Name);
                    divTag.setAttribute("itemid", element.Id);
                    divTag.setAttribute("version", element.Version);
                    divTag.setAttribute("ispublic", element.IsPublic);
                    document.getElementById("panel").appendChild(divTag);
                });
            }
        }
        
        // 1. Main function to call
        function getDashboardAccessToken(dashboardId, mode, expirationTime) {
          const queryString = generateEmbedQueryString(dashboardId, mode, expirationTime);
          const payload = buildRequestPayload(queryString);
          sendAuthorizationRequest(payload, dashboardId);
        }
        // 2. Build embed query string
        function generateEmbedQueryString(dashboardId, mode, expirationTime) {
          return [
            `embed_nonce=${generateUUID()}`,
            `embed_dashboard_id=${dashboardId}`,
            `embed_mode=${mode}`,
            `embed_timestamp=${Math.floor(Date.now() / 1000)}`,
            `embed_expirationtime=${expirationTime}`
          ].join('&');
        }
        
        // 3. Generate UUID
        function generateUUID() {
          return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            const r = Math.random() * 16 | 0;
            const v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
          });
        }
        
        // 4. Build request payload
        function buildRequestPayload(queryString) {
          return {
            embedQuerString: queryString,
            dashboardServerApiUrl: `${rootUrl}/api/${siteIdentifier}`
          };
        } 
    
  • This query is sent to the embed authorization API, which returns a valid access token with its expiration time.

         function sendAuthorizationRequest(payload, dashboardId) {
           $.ajax({
             url: authorizationServerUrl,
             type: "POST",
             async: true,
             data: JSON.stringify(payload),
             contentType: "application/json",
            success: function (response) {
                 try {
                     const parsed = typeof response === 'string' ? JSON.parse(response) : response;
                     let accessToken, expires;
                     console.log(parsed);
         
                     if (Array.isArray(parsed?.Data)) { // For multiTab dashboard
                         for (const item of parsed.Data) {
                             if (item.access_token) {
                                 accessToken = item.access_token;
                                 isMultiTab = true;
                                 expires = item[".expires"];
                                 break;
                             }
                         }
                     } 
                     else{
                         accessToken = parsed.Data.access_token;
                         expires = parsed.Data[".expires"];
                     }
         
                     if (accessToken) {
                         token = accessToken;
                         tokenExpiry = new Date(expires);
                         renderDashboard(dashboardId);
                     } else {
                         alert("Access token not found in response.");
                     }
                 } catch (error) {
                     alert("Error parsing response:", error);
                 }
                 },
           });
         } 
    
  • During this API call, you can apply row-level security, group-based authorization, and anonymous user to generate the token.

        [HttpPost]
        [Route("AuthorizationServer")]
        public string AuthorizationServer([FromBody] object embedQuerString)
        {
            var embedClass = Newtonsoft.Json.JsonConvert.DeserializeObject<EmbedClass>(embedQuerString.ToString());
    
            var embedQuery = embedClass.embedQuerString;
            // User your user-email as embed_user_email
            embedQuery += "&embed_user_email=" + GlobalAppSettings.EmbedDetails.UserEmail;
    		//To set embed_server_timestamp to overcome the EmbedCodeValidation failing while different timezone using at client application.
            double timeStamp = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
            embedQuery += "&embed_server_timestamp=" + timeStamp;
            var embedDetailsUrl = "/embed/authorize?" + embedQuery + "&embed_signature=" + GetSignatureUrl(embedQuery);
    
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(embedClass.dashboardServerApiUrl);
                client.DefaultRequestHeaders.Accept.Clear();
    
                var result = client.GetAsync(embedClass.dashboardServerApiUrl + embedDetailsUrl).Result;
                string resultContent = result.Content.ReadAsStringAsync().Result;
                return resultContent;
            }
        } 
    
  • The returned access token is stored globally and reused for rendering dashboards.

  • The renderDashboard() method uses this token with the Bold BI JavaScript SDK (embedToken property).

        function renderDashboard(dashboardId) {
          if (!token || isTokenExpired()) {
            console.log("Token expired or missing. Fetching new token...");
            getDashboardAccessToken(dashboardId, 'view', '100000');
          }
            this.dashboard = BoldBI.create({
                serverUrl: rootUrl + "/" + siteIdentifier,
                dashboardId: dashboardId,
                embedContainerId: "dashboard",
                embedToken: token,
                isMultiTabDashboard: isMultiTab
            });
        
            this.dashboard.loadDashboard();
        };
        function isTokenExpired() {
          if (!tokenExpiry) return true;
          return new Date() >= new Date(tokenExpiry);
        } 
    
  • Rather than generating a new token for each dashboard load, the application reuses the same token to support:

    • Row-level security (RLS)
    • Group-based authorization
    • Anonymous user access

Supported Scenarios with embedToken

  • For Bold BI users
    • Dashboard viewer
    • Dashboard Designer
    • Multi-tab dashboard
  • For anonymous users
    • Dashboard viewer

Note: To render multi tab dashboard we have to set the isMultiTabDashboard as true in the BoldBI.Create method

Was this article useful?
Like
Dislike
Help us improve this page
Please provide feedback or comments
DV
Written by Dharshini Vivekanandan
Updated:
Comments (0)
Please  to leave a comment
Access denied
Access denied