Want to embed multiple dashboards without generating a new token every time? This guide shows you how to:
Do you really need to call the server for every dashboard render?
Nope! You can fetch the dashboard list once, reuse the token, and render dashboards on demand.
Use this AJAX call to get the list of dashboards:
function Init() {
const 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(http.response);
}
};
http.send();
}
To retrieve the list of dashboards from the Bold BI server, we are using v5.0 Rest API:
[HttpGet]
[Route("GetDashboards")]
public string GetDashboards() {
var token = GetToken();
using (var client = new HttpClient()) {
client.BaseAddress = new Uri(GlobalAppSettings.EmbedDetails.ServerUrl);
client.DefaultRequestHeaders.Add("Authorization", $"{token.TokenType} {token.AccessToken}");
var result = client.GetAsync($"/api/{GlobalAppSettings.EmbedDetails.SiteIdentifier}/v5.0/dashboards").Result;
return JObject.Parse(result.Content.ReadAsStringAsync().Result)["Data"].ToString();
}
}
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;
}
}
Render the dashboard list in the sidebar and load the first dashboard:
function ListDashboards(data) {
if (data?.length) {
getDashboardAccessToken(data[0].Id, 'view', '100000');
data.forEach(d => {
const div = document.createElement("div");
div.innerHTML = d.Name;
div.className = "dashboard-item";
div.setAttribute("onclick", `renderDashboard('${d.Id}')`);
document.getElementById("panel").appendChild(div);
});
}
}
Create a query string that includes the dashboard ID, access mode, expiration time, and a unique nonce. This ensures each request is secure and time-bound.
function getDashboardAccessToken(dashboardId, mode, expirationTime) {
const queryString = generateEmbedQueryString(dashboardId, mode, expirationTime);
const payload = buildRequestPayload(queryString);
sendAuthorizationRequest(payload, dashboardId);
}
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('&');
}
A UUID is used as a nonce to uniquely identify each request.
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);
});
}
The payload includes the query string and the API URL of the Bold BI server.
function buildRequestPayload(queryString) {
return {
embedQuerString: queryString,
dashboardServerApiUrl: `${rootUrl}/api/${siteIdentifier}`
};
}
Send the payload to your backend authorization server, which will generate the token and return it to the client.
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);
}
},
});
}
On the server side, the embed query is signed and sent to the Bold BI server to retrieve the token.
[HttpPost]
[Route("AuthorizationServer")]
public string AuthorizationServer([FromBody] object embedQuerString)
{
var embedClass = JsonConvert.DeserializeObject<EmbedClass>(embedQuerString.ToString());
var embedQuery = embedClass.embedQuerString;
embedQuery += "&embed_user_email=" + GlobalAppSettings.EmbedDetails.UserEmail;
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;
return result.Content.ReadAsStringAsync().Result;
}
}
The renderDashboard() method uses the generated token via the embedToken property. It also checks the token’s expiration time and automatically fetches a new token if the current one has expired.
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);
}