This guide covers how to generate secure, time-limited tokens for embedding dashboards in your application.
Overview
Iframe tokens allow you to securely embed Papermap dashboards in your application. Each token:
Is signed with HMAC-SHA256 for security
Has a configurable expiration time
Is specific to a tenant and dashboard
Can only be used for the specified dashboard
Token Structure
The token contains:
api_key_id: Your API key identifier
workspace_id: Your workspace ID
tenant_id: The tenant identifier
dashboard_id: The specific dashboard ID
valid_until: Unix timestamp when token expires
signature: HMAC signature for verification
Generating Iframe Tokens
This token generation is different from the one in the Basic
Embedding section. The token
generation in this section is for multi-tenant iframe embedding.
Python
TypeScript/Node.js
PHP
Java
C#
Go
import json
import base64
import time
def generate_iframe_token (
tenant_id : str ,
workspace_id : str ,
dashboard_id : str ,
valid_until : Optional[ int ] = None
):
# 1. Get tenant's dashboard
tenant_dashboard = TenantDashboard.read(
session = db,
filter_by = { "tenant_id" : tenant_id}
)
if not tenant_dashboard:
raise Exception ( "Dashboard not found for tenant" )
# 2. Set expiration (default 1 hour)
if valid_until is None :
valid_until = int (time.time()) + 3600
# 3. Create HMAC signature
payload = f " { workspace_id }{ valid_until } "
signature = hmac.new(
SECRET_KEY .encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
# 4. Encode token
token_data = {
"api_key_id" : API_KEY_ID ,
"workspace_id" : workspace_id,
"tenant_id" : tenant_id,
"dashboard_id" : dashboard_id,
"valid_until" : valid_until,
"signature" : signature
}
token_json = json.dumps(token_data)
encoded_token = base64.urlsafe_b64encode(token_json.encode()).decode()
return {
"token" : encoded_token,
"dashboard_id" : dashboard_id,
"expires_at" : valid_until
}
Using the Token in Frontend
Once you have the token, embed the dashboard using an iframe:
< iframe
src = "https://papermap.ai/embedded/dashboard?embedded-token={YOUR_TOKEN}"
width = "100%"
height = "600px"
frameborder = "0"
></ iframe >
React Example
import React , { useState , useEffect } from "react" ;
function DashboardEmbed ({ tenantId , workspaceId , dashboardId }) {
const [ token , setToken ] = useState ( null );
const [ loading , setLoading ] = useState ( true );
useEffect (() => {
async function fetchToken () {
const response = await fetch ( "/api/dashboards/iframe-token" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({ tenantId , workspaceId , dashboardId }),
});
const data = await response . json ();
setToken ( data . token );
setLoading ( false );
}
fetchToken ();
}, [ tenantId , workspaceId , dashboardId ]);
if ( loading ) return < div > Loading dashboard... </ div > ;
return (
< iframe
src = { `https://papermap.ai/embedded/dashboard?embedded-token= ${ token } ` }
width = "100%"
height = "600px"
frameBorder = "0"
title = "Dashboard"
/>
);
}
Workflow Diagram
1. Client → Request Embed Token
POST /api/v1/dashboards/iframe-token
{
"tenant_id": "org-123",
"workspace_id": "workspace-456"
}
2. Your API → Generate Token
- Lookup dashboard_id for tenant
- Create HMAC signature
- Encode as base64 token
3. Return Token to Client
{
"token": "eyJhcGlfa2V5X2lkIjoi...",
"dashboard_id": "dashboard-789",
"expires_at": 1700000000
}
4. Frontend → Embed in Iframe
<iframe src="https://papermap.ai/embedded/dashboard?embedded-token=eyJhcGlfa2V5X2lkIjoi..." />
Token Refresh
Since tokens expire, implement a refresh mechanism:
class DashboardTokenManager {
private token : string | null = null ;
private expiresAt : number | null = null ;
async getToken ( tenantId : string , workspaceId : string , dashboardId : string ) {
// Check if token is still valid (with 5 min buffer)
if (
this . token &&
this . expiresAt &&
this . expiresAt > Date . now () / 1000 + 300
) {
return this . token ;
}
// Fetch new token
const response = await fetch ( "/api/dashboards/iframe-token" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({ tenantId , workspaceId , dashboardId }),
});
const data = await response . json ();
this . token = data . token ;
this . expiresAt = data . expires_at ;
return this . token ;
}
}
Security Considerations
Always verify tenant ownership before generating tokens Never generate a token without verifying that the requesting user has access to the tenant’s dashboard.
Tenant Access Verification
# Good - Verify tenant access
if current_user[ "organization_id" ] != tenant_id:
raise HTTPException( status_code = 403 , detail = "Access denied" )
# Then generate token
token = generate_iframe_token(tenant_id, workspace_id, dashboard_id)
Token Expiration
Advisable: 1 hour (3600 seconds)
Configurable based on your security requirements
Shorter expiration = more secure, but requires more frequent refreshes
Longer expiration = better UX, but higher security risk
Best Practices
Generate tokens on-demand : Don’t pre-generate and store tokens
Use HTTPS : Always serve your application over HTTPS
Implement token refresh : Refresh tokens before they expire
Log token generation : Monitor for unusual access patterns
Rate limit : Prevent abuse by rate limiting token generation
Next Steps