Securely Embedding Components (Session Tokens)

SignStack provides embeddable components that you can integrate directly into your application—like signing experiences or workflow editing interfaces. To securely power these embedded experiences, SignStack uses Embed Tokens (also called Session Tokens).

This guide explains how embed tokens work and how to use them securely.

What Are Embed Tokens?

Embed tokens are short-lived, scoped JWT tokens designed specifically for frontend embedded components. Unlike API Keys (which are long-lived secrets for server-to-server communication), embed tokens are safe to use in browser environments because they:

  • Expire quickly (typically 15-60 minutes)
  • Are scoped to a specific resource (e.g., one workflow)
  • Have limited permissions (only what's needed for that embed intent)
  • Can be restricted by origin (CORS protection)

Embed Intents

SignStack supports different embed intents, each designed for a specific use case:

1. Signing Session (signing_session)

Used when a participant needs to sign documents within your application.

{
  "intent": "signing_session",
  "workflowId": "wf_abc123",
  "stepKey": "signer1",
  "expiresIn": 900,
  "allowedOrigins": ["https://yourapp.com"],
  "context": {
    "receipientEmail": "signer@example.com",
    "receipientName": "John Doe"
  }
}

2. Workflow Editing (workflow_editing)

Used when users need to edit or review a workflow's documents and data.

{
  "intent": "workflow_editing",
  "workflowId": "wf_abc123",
  "expiresIn": 3600,
  "allowedOrigins": ["https://yourapp.com"]
}

Generating Embed Tokens

Embed tokens are generated server-side using your API Key, then passed to your frontend.

Step 1: Request an Embed Token (Server-Side)

curl -X POST https://api.signstack.ai/v1/orgs/{orgId}/auth/embed \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": "signing_session",
    "workflowId": "wf_abc123",
    "stepKey": "signer1",
    "expiresIn": 900,
    "allowedOrigins": ["https://yourapp.com"],
    "context": {
      "receipientEmail": "signer@example.com"
    }
  }'

Step 2: Response

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "tokenType": "Bearer",
  "expiresIn": 900,
  "expiresAt": "2024-01-15T10:30:00Z",
  "scopes": ["workflow:sign"],
  "subject": {
    "type": "embed",
    "id": "embed_xyz789",
    "organizationId": "org_123"
  },
  "resources": [
    {
      "type": "workflow",
      "id": "wf_abc123",
      "steps": ["signer1"],
      "actions": ["sign", "view"]
    }
  ]
}

Step 3: Pass to Frontend

Pass the accessToken to your frontend component. Never expose your API Key or server access token to the frontend.

// Your backend endpoint
app.post('/api/get-signing-token', async (req, res) => {
  const { workflowId, stepKey, signerEmail } = req.body;

  // Get embed token from SignStack
  const embedToken = await signstack.auth.createEmbedToken({
    intent: 'signing_session',
    workflowId,
    stepKey,
    context: { receipientEmail: signerEmail }
  });

  // Return only the token to the frontend
  res.json({ token: embedToken.accessToken });
});

Embedding the Component

Use the embed token in your frontend to initialize SignStack components:

<div id="signstack-signing"></div>

<script>
  const signingComponent = SignStack.createSigningSession({
    container: '#signstack-signing',
    token: embedToken, // The token from your backend
    onComplete: (result) => {
      console.log('Signing completed:', result);
    },
    onError: (error) => {
      console.error('Signing error:', error);
    }
  });
</script>

Security Best Practices

1. Always Generate Tokens Server-Side

Never expose your API Key or long-lived access tokens to the frontend. Always have your backend generate embed tokens and pass only those to the client.

[User Browser] → [Your Backend] → [SignStack API]
                 ↑                  ↓
            API Key used       Embed Token returned

         [User Browser]
         Embed Token used

2. Use Short Expiration Times

Set expiresIn to the minimum time needed for the task:

  • Signing sessions: 15-30 minutes
  • Workflow editing: 30-60 minutes

3. Restrict Allowed Origins

Always specify allowedOrigins to prevent your embed tokens from being used on unauthorized domains:

{
  "allowedOrigins": [
    "https://yourapp.com",
    "https://staging.yourapp.com"
  ]
}

4. Validate Context Data

When generating tokens, validate that the requesting user has permission to access the specified workflow and step:

// Verify user has permission before generating token
const workflow = await getWorkflow(workflowId);
if (workflow.organizationId !== user.organizationId) {
  throw new ForbiddenError('Access denied');
}

5. Handle Token Expiration Gracefully

Embed components should handle expired tokens gracefully:

SignStack.createSigningSession({
  token: embedToken,
  onTokenExpired: async () => {
    // Fetch a new token from your backend
    const newToken = await fetchNewToken();
    return newToken;
  }
});

Token Payload Structure

Embed tokens contain specific claims that identify their purpose:

Claim Description
sub Embed session identifier
org_id Organization ID
embed_type The embed intent (signing_session or workflow_editing)
workflow_id The specific workflow this token grants access to
step_key For signing: the participant step (optional)
allowed_origins CORS-allowed domains
scopes Permitted actions (e.g., workflow:sign, workflow:edit)
exp Expiration timestamp

Comparison: API Tokens vs. Embed Tokens

Aspect API Access Token Embed Token
Use Case Server-to-server API calls Frontend embedded components
Lifespan ~1 hour 15-60 minutes
Scope Organization-wide (based on roles) Single resource (workflow)
Generated From API Key Access Token + embed request
Safe for Frontend? No Yes
Origin Restrictions No Yes (optional)

Common Use Cases

Embedded Signing in Your App

Allow customers to sign agreements without leaving your application:

  1. User initiates signing in your app
  2. Your backend creates a workflow and gets an embed token
  3. Frontend renders the signing component with the token
  4. User signs, component fires onComplete
  5. Your backend receives webhook notification

Self-Service Document Review

Let users review and edit their workflow data:

  1. User clicks "Edit Agreement" in your app
  2. Backend generates a workflow_editing embed token
  3. Frontend shows the editing interface
  4. User makes changes, saves
  5. Changes are persisted to the workflow

Troubleshooting

Token Rejected (401)

  • Check that the token hasn't expired
  • Verify allowedOrigins includes your domain
  • Ensure the workflow still exists and is accessible

CORS Errors

  • Add your domain to allowedOrigins when generating the token
  • Include both production and staging domains if needed

Component Not Loading

  • Verify the token was generated for the correct intent
  • Check browser console for JavaScript errors
  • Ensure the SignStack SDK is properly loaded