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:
- User initiates signing in your app
- Your backend creates a workflow and gets an embed token
- Frontend renders the signing component with the token
- User signs, component fires
onComplete - Your backend receives webhook notification
Self-Service Document Review
Let users review and edit their workflow data:
- User clicks "Edit Agreement" in your app
- Backend generates a
workflow_editingembed token - Frontend shows the editing interface
- User makes changes, saves
- Changes are persisted to the workflow
Troubleshooting
Token Rejected (401)
- Check that the token hasn't expired
- Verify
allowedOriginsincludes your domain - Ensure the workflow still exists and is accessible
CORS Errors
- Add your domain to
allowedOriginswhen 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