Skip to main content
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.
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 tokensNever 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

  1. Generate tokens on-demand: Don’t pre-generate and store tokens
  2. Use HTTPS: Always serve your application over HTTPS
  3. Implement token refresh: Refresh tokens before they expire
  4. Log token generation: Monitor for unusual access patterns
  5. Rate limit: Prevent abuse by rate limiting token generation

Next Steps