Embedding Bold BI Dashboard in ASP .NET Core Applications with Embed Token Authentication
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
- .NET 8.0 SDK
- Visual Studio Code or another C# IDE
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.
-
To download the
embedConfig.json
file, please follow this link for reference. Additionally, you can refer to the following image for visual guidance.
-
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.
-
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 thecommand 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
, theGetDashboards()
action uses theGetToken
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