Many customers want a simple, but secure way to embed content that can be accessed by both external (users who do not have a registered account in Sigma) and internal user (users who access Sigma only through an embed, inside a parent application).
To enable this, Sigma supports authenticating secure embeds using JSON Web Tokens (JWTs). Signing your secure embed URLs with JWTs has several advantages, but a few limitations too:
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
The remaining discussion in this section is intended for those less familiar with JWT, how it is structured, the transaction flow and benefits. Feel free to jump to the next section if you just want a demonstration of JWT in Sigma.
A JWT is composed of three parts: Header
, Payload
, and Signature
, which are concatenated with dots (.) to form a single string:
Header: Typically consists of two parts: the type of token (JWT) and the signing algorithm (e.g., HMAC SHA256).
Payload: Contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private claims.
Signature: Used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way.
1: Registered Claims: These are predefined, standardized claims that are recommended to be used in a JWT. They have specific purposes and are commonly recognized across implementations.
Examples:
2. Public Claims: These are claims that can be defined at will by those using JWTs.
Examples:
3. Private Claims: These are custom claims created to share information between parties that agree on using them but do not need to be registered or standardized. They are specific to the application and are not meant to be shared beyond the agreed-upon parties.
Examples:
For more information on JWT claims in Sigma, see here.
Let's walk through a typical workflow where a client requests an embedded content using a JSON Web Token (JWT) and how the server handles this request, validates it, and serves the embedded content based on the credentials and environment variables.
Upon receiving a request from the client for a URL to access embedded Sigma content, the server securely generates a JWT by combining the configured embed client credentials with known user claims (such as email, account type, and team). This signed and encrypted JWT is then embedded into the URL returned to the client, enabling secure and authorized access to the content.
Expanding on this description a bit more:
The client (e.g., a web application or another service) sends a request to the server to obtain a URL for accessing embedded Sigma content. This request might include minimal information, such as the user's identity or a general request for access.
Credential Handling: The server securely manages the necessary credentials, such as the secret key (EMBED_SECRET) and client ID (EMBED_CLIENT_ID), that are required to generate the JWT.
User Claims: The server retrieves or determines relevant claims about the user, such as their email address (sub), roles, or teams. These claims are crucial for defining the user's permissions and the scope of access within the embedded content.
JWT Creation: The server uses the credentials and claims to create a JWT. This JWT is signed with the secret key, ensuring its authenticity. The JWT includes claims that specify who the user is, what they are allowed to do, and how long their access is valid.
The server constructs a signed URL that includes the JWT as a query parameter. This URL is then sent back to the client.
Authorization: When the client uses this URL to access the embedded content, the JWT is used by the Sigma server to verify the user's identity and permissions, granting or denying access accordingly.
Developers evaluating Sigma embedding and the security options.
While you may clone the entire repository (it is not that large), we want to avoid cloning sections of the repository that are not of immediate interest.
Instead, we will use VSCode and terminal to perform a git sparse-checkout
of the specific project folder we are interested in. A few extra steps, but cleaner local project folder.
Open VSCode
and a new terminal
session.
In terminal, navigate to the desired directory where we want to clone the repo folder into.
For example:
cd {/path/to/your/directory}
Make a new directory:
mkdir sigma_secure_embed_JWT
Change to the new directory:
cd sigma_secure_embed_JWT
Execute the terminal command:
git init
Add the remote repository as the origin:
git remote add -f origin https://github.com/sigmacomputing/quickstarts-public.git
Enable sparse checkout:
git config core.sparseCheckout true
Specify the folder you want to clone by adding it to the sparse-checkout configuration:
echo "sigma_secure_embed_JWT/" >> .git/info/sparse-checkout
At this point, we have run each command and not seen any errors:
Finally, pull the specified folder from the repository:
git pull origin main
After the command runs, click the button to Open Folder
:
Navigate to where your folder is and click Open
:
For example, our folder is in the GitHub
folder on the local machine:
We can now see the project called sigma_secure_embed_JWT
:
The project has almost everything we need, but we will need some embedded content and credentials from Sigma before we can test this out.
We will create the required information to pass to the developer of the Parent application (in this case the developer is us).
Embed Client credentials
("credentials") are a crucial component for creating a Sigma embed. These credentials are encoded within your embed URL, providing an additional layer of validation to ensure the embed's authenticity and security.
The credentials are made up of the Client ID
and Client Secret
, and are generated using the Sigma UI.
The credentials will be used in the embed API, to ensure your embed URLs are valid at run-time.
Here are some important points to note about the credentials:
Irretrievable: Once created, you cannot retrieve the original credentials. Ensure to store them securely.
Regeneration: If the credentials are lost, they can be regenerated, but with different values. This will invalidate all existing embeds until the new values are in place in the embed API.
Update Requirement: After regenerating new credentials, you must update all existing embeds, using the Embed API for that change.
Remember to keep your credentials in a secure location, as losing them requires action to maintain your embedded analytics functionality.
We will now create credentials that are specific to our workbook embed.
1: Navigate to Administration
> Developer Access
:
2: Click Create New
, located in the page's top right corner. This will open the Create Client Credentials
modal.
3: Under Access Credential Type?
select Embedding
.
4: Enter a Name
and Description
as you see fit.
5: Under Owner
, select an organization member with the account type you would like to associate with the embed secret. For now, just select yourself or an Administrator
.
6: Click Create:
7: Copy the provided ClientID
and Secret
and store them.
8: Click Close.
Paste the credentials into a text file for now, we will use them later.
This embedding method allows you to use any Sigma URL to embed, assuming the proper permissions are passed along with it as parameters.
Sigma is very flexible and has different workflows for creating content, based on source data.
For example, we could first create a data model, set permission on it, and then save it off for later use in a workbook(s). We would then create a workbook with a table, that shows data from the data model we saved earlier.
To minimize the steps, we will leverage a different workflow.
To demonstrate our embed, we need to create a Team to share our new workbook with.
Navigate to Administration
> Teams
and click Create Team
.
Name the new team Sales_People
, and click Create
.
From Sigma / Home
click the +
Create New button and click Workbook
:
We will use Table
from the options:
Next we need to select our source data. We will use the typical sample data, as in other QuickStarts.
Sigma allows users to search for tables by name; type Hands
in the search bar and select the PLUGS_ELECTRONICS_HANDS_ON_LAB_DATA
table:
This opens the selected table in a new (unpublished) workbook that carries the temporary name Exploration
:
The first thing we want to do is click the Save As
button, choose where to store the workbook and give it the name My Plugs Sales Table - JWT
and save it.
You may have noticed that Sigma provides folders
, a My Documents
folder and also workspaces. This enables a variety of use-cases to be possible with regards to how documents are stored, managed and shared with others.
For those users who have used Sigma embedding before, this step is a little different. We used to use Sigma's UI to create a "Workbook URL" > "Embed Path". This is no longer required when using JWT.
With JWT embedding, we simply use the Published
url, taken directly from the browser.
In light of this change, our Git project will refer to this URL as the BASE_URL
.
Go to the Published
version of our workbook:
Copy the URL of to a text file, so that we can reference it in the embedding script later:
Click the caret to the right of the workbook's name and select Share
Search for the Sales_People
team, select it and provision Can view
permission:
We are now ready to configure our secure embed for our workbook and team.
Return to VSCode and open the file .env
. It will have placeholders for the values we need to provide:
Replace the three placeholders with the values we saved from earlier steps:
For example:
We have commented the code in the Embed-API script so that you can understand what it is doing at each step. The code is shown below, although you can just review it in VSCode as well.
// embed-api.js
const jwt = require('jsonwebtoken'); // Import jsonwebtoken library for handling JWTs
const { v4: uuid } = require('uuid'); // Import uuid for generating unique identifiers
const dotenv = require('dotenv'); // Import dotenv for loading environment variables
dotenv.config(); // Load environment variables from .env file
// Define constants for the embed URL and session length
const BASE_URL = process.env.BASE_URL;
const SESSION_LENGTH = Math.min(process.env.SESSION_LENGTH || 3600, 2592000); // Max 30 days in seconds
// Log important configuration details to ensure they are correctly set
console.log('BASE_URL:', BASE_URL);
console.log('SESSION_LENGTH:', SESSION_LENGTH);
console.log('EMBED_CLIENT_ID:', process.env.EMBED_CLIENT_ID); // Verify the client ID
// Function to generate a signed URL for embedding Sigma dashboards
async function generateSignedUrl() {
try {
// Retrieve the secret and email from environment variables
const secret = process.env.EMBED_SECRET;
const email = process.env.EMBED_EMAIL;
const time = Math.floor(Date.now() / 1000); // Generate the current time as a Unix timestamp
// Generate JWT with claims
// See https://help.sigmacomputing.com/docs/create-an-embed-api-with-json-web-tokens for list of available claims
const token = jwt.sign({
sub: email, // Subject (the email of the user)
iss: process.env.EMBED_CLIENT_ID, // Issuer (client ID)
jti: uuid(), // JWT ID (unique identifier for the token)
iat: time, // Issued at time (current time)
exp: time + SESSION_LENGTH, // Expiration time (current time + session length)
account_type: "lite", // Optional claim for account type
teams: ["Sales_People"] // Optional claim for user teams
}, secret, {
algorithm: 'HS256', // Algorithm used for signing the JWT
keyid: process.env.EMBED_CLIENT_ID // Key ID for the JWT header, should match Sigma's expectations
});
// Decode the JWT to inspect its content and log it
const decodedToken = jwt.decode(token, { complete: true });
console.log('Decoded JWT:', decodedToken); // Log the decoded JWT for debugging
// Construct the signed embed URL by appending the JWT and embed parameters
const signedEmbedUrl = `${BASE_URL}?:jwt=${token}&:embed=true`;
// Log the constructed signed URL
console.log('Signed Embed URL:', signedEmbedUrl);
return signedEmbedUrl; // Return the signed embed URL
} catch (error) {
// Log any errors that occur during JWT generation
console.error("Failed to generate JWT:", error);
throw new Error("JWT generation failed"); // Throw an error if JWT generation fails
}
}
// Export the generateSignedUrl function so it can be used in other files
module.exports = { generateSignedUrl };
In VSCode, open a new terminal session, make sure to be in the correct folder (as shown in the screenshot below).
Run the command:
nodemon server.js
In a browser, browse to:
http://locahost:3000
We now see our workbook embedded into a parent application. The parent only has a header of Sigma Embed with JWT
but as a demonstration, this is sufficient:
We have include code that returns key values in the terminal console, for debugging and demonstration purposes only. These would normally not be included in production code and are not required to make this work. We felt is was useful to see these values to better understand what is being passed.
For example, my log of the last step looks like this:
We use the the jwt.decode()
function that is provided by the jsonwebtoken
library to decodes a JWT without verifying its signature.
It simply parses the JWT and returns its payload (the claims it contains) along with the header and signature if you use the complete.
An alternative method is to use a third-party website to decode the token. For example JWT Debugger can decode the token just ass we have done in our sample code:
Sigma allows a workbook, page or single element to be embedded.
In the case of single elements and workbook pages, we need to manually adjust the copied url (at the time of this QuickStart). This will be automated in the near term in the Sigma UI.
The correct URL syntax that is required by the Embed_API is:
https://app.sigmacomputing.com/{organization-name}/workbook/{workbookname}-{workbookUrlId}/element/{elementId}
If we add a quick bar chart to the workbook, based on the table data:
We can select the bar chart and grab it's URL, and format using the syntax described above.
https://app.sigmacomputing.com/XXXXXXXX/workbook/My-Plugs-Sales-Table-JWT-28xgywpg21TkRWtz3jVRCe/element/bAGJb_-0AS
Paste the URL into the .env
file, over-writing the existing value for EMBED_URL
and save the file.
Refresh the browser page to see the single element embed:
We can repeat the single element workflow, adjusting the workbook to have a second page and using the page's URL in .env.
The correct syntax for constructing the page URL is:
https://app.sigmacomputing.com/{organization-name}/workbook/{workbookname}-{workbookUrlId}/page/{pageId}
We added a second page to our workbook that looks like this:
After adjusting the .env
file for the page URL, the embed looks like this:
Our .env looks like this (just for your information):
# JWT .env file
# Sigma embed configuration - REQUIRED parameters:
# Workbook:
#BASE_URL=https://app.sigmacomputing.com/XXXXXX/workbook/My-Plugs-Sales-Table-JWT-28xgywpg21TkRWtz3jVRCe
# Element
#BASE_URL=https://app.sigmacomputing.com/XXXXXX/workbook/My-Plugs-Sales-Table-JWT-28xgywpg21TkRWtz3jVRCe/element/bAGJb_-0AS
#Page (this one is enabled for testing...)
#BASE_URL=https://app.sigmacomputing.com/XXXXXX/workbook/My-Plugs-Sales-Table-JWT-28xgywpg21TkRWtz3jVRCe/page/GHs4vJQxig
...the rest of the file's contents
In this QuickStart, we explained and demonstrated how to use JSON Web Tokens (JWT) for embedding content in Sigma.
Additional Resource Links
Blog
Community
Help Center
QuickStarts
Be sure to check out all the latest developments at Sigma's First Friday Feature page!