Security & Authentication
Comprehensive guide to securing Elsa Server and workflows end-to-end, covering identity, authentication, tokenized resume URLs, CORS, secrets management, and production hardening.
Executive Summary
This guide provides actionable, Elsa-specific security practices for protecting your workflow runtime, API endpoints, and Studio deployments. It focuses on configurations and patterns unique to Elsa Workflows while deferring deep platform-specific topics (OIDC provider setup, full reverse proxy hardening, WAF configuration) to official vendor documentation.
Goals and Scope
What This Guide Covers:
Configuring Identity and Authentication in Elsa Server (
UseIdentity,UseDefaultAuthentication)Securing tokenized bookmark resume URLs (TTL, revocation, rate limiting)
CORS, CSRF, and rate limiting for public-facing endpoints
Secrets management for API keys, database connections, and workflow variables
Network security and TLS requirements
Studio deployment security considerations
Production hardening checklist
What This Guide Defers to Vendor Documentation:
Detailed OIDC provider configuration (Azure AD, Auth0, Keycloak)
Full reverse proxy hardening (Nginx, Traefik, Envoy)
Web Application Firewall (WAF) setup
Infrastructure-as-Code (IaC) security best practices
Advanced certificate management and PKI
For clustering-specific security (distributed lock credentials, database permissions), see Clustering Guide (DOC-015).
Identity & Authentication in Elsa Server
UseIdentity Configuration
Elsa Server's identity system is configured in Program.cs via the UseIdentity extension method. This setup supports API keys, JWT tokens, and OIDC integration.
Reference: src/apps/Elsa.Server.Web/Program.cs in elsa-core
Basic Identity Setup:
using Elsa.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Configure Elsa with identity
builder.Services.AddElsa(elsa =>
{
elsa
.UseIdentity(identity =>
{
// Load identity configuration from appsettings.json
identity.UseConfigurationBasedIdentityProvider();
// Configure token options
identity.ConfigureTokenOptions(options =>
{
options.AccessTokenLifetime = TimeSpan.FromHours(1);
options.RefreshTokenLifetime = TimeSpan.FromDays(7);
});
})
.UseDefaultAuthentication() // Enables JWT/API key authentication
.UseWorkflowManagement()
.UseWorkflowRuntime()
.UseWorkflowsApi();
});
var app = builder.Build();
// Enable authentication middleware
app.UseAuthentication();
app.UseAuthorization();
app.UseWorkflowsApi();
app.Run();Key Configuration Points:
UseIdentity: Registers Elsa's built-in identity systemUseConfigurationBasedIdentityProvider: Loads identity providers fromappsettings.jsonUseDefaultAuthentication: Enables API key and JWT bearer authenticationToken Lifetimes: Configure appropriate TTLs for access and refresh tokens
Minimal appsettings.json Structure
See examples/appsettings-identity.json for a complete example with placeholders.
{
"Elsa": {
"Identity": {
"Providers": [
{
"Type": "ApiKey",
"Name": "ApiKeyProvider",
"Options": {
"ApiKeys": [
{
"Key": "${API_KEY_1}", // Use environment variable
"Roles": ["Admin"]
}
]
}
},
{
"Type": "Jwt",
"Name": "JwtProvider",
"Options": {
"Issuer": "https://your-identity-provider.com",
"Audience": "elsa-workflows",
"SigningKey": "${JWT_SIGNING_KEY}" // Never commit this!
}
}
]
}
}
}Important:
Never commit secrets to source control
Use environment variables or secret managers (Azure Key Vault, AWS Secrets Manager, HashiCorp Vault)
Rotate API keys regularly (every 90 days or per your security policy)
API Key vs JWT/OIDC
API Keys:
Best for: Machine-to-machine communication, CI/CD pipelines, internal services
Pros: Simple to implement, no external dependencies
Cons: No fine-grained scopes, harder to rotate safely
JWT/OIDC:
Best for: User authentication, web applications, mobile apps
Pros: Industry standard, fine-grained scopes, short-lived tokens, refresh token support
Cons: Requires OIDC provider setup and maintenance
Recommendation: For production systems, prefer JWT/OIDC for user-facing endpoints and reserve API keys for trusted service-to-service communication.
Tokenized Bookmark Resume URLs
How Bookmark Tokens Work
Elsa generates tokenized URLs for resuming workflows at bookmarks (e.g., HTTP callbacks, webhooks). These URLs are used in scenarios like:
Sending approval requests via email
Webhook callbacks from external systems
Multi-step form submissions with state preservation
Reference: src/modules/Elsa.Http/Extensions/BookmarkExecutionContextExtensions.cs (elsa-core)
The GenerateBookmarkTriggerUrl method creates URLs in this format:
POST /elsa/api/bookmarks/resume?t=<encrypted_token>The token contains:
Bookmark ID
Workflow instance ID
Expiration timestamp
Optional payload data
Security Best Practices for Resume Tokens
See examples/resume-endpoint-notes.md for detailed guidance.
Critical Security Controls:
Time-to-Live (TTL):
Bookmark tokens generated by
GenerateBookmarkTriggerUrlare encrypted and contain bookmark metadataToken lifetime is not configurable separately; implement expiration at the bookmark level
Use short-lived bookmarks for webhooks (minutes) and longer-lived for email approvals (hours/days)
Consider implementing custom token expiration logic if needed for your security requirements
Tokens become invalid when the bookmark is consumed (AutoBurn) or the workflow is cancelled
Single-Use Semantics:
Bookmarks are "burned" (deleted) after successful resume by default
Use
AutoBurn = truein bookmark creation for one-time-use tokensCheck for duplicate resume attempts via distributed locking
Revocation Strategy:
Implement a token revocation list for compromised tokens
Workflow cancellation automatically invalidates associated bookmarks
Manual revocation: delete bookmark from database or cancel workflow
Audit Logging:
// Log resume attempts _logger.LogInformation( "Bookmark resume attempted. BookmarkId={BookmarkId}, WorkflowInstanceId={InstanceId}, Success={Success}", bookmarkId, instanceId, success);Log all resume attempts (success and failure)
Include source IP, timestamp, and workflow context
Integrate with SIEM for anomaly detection
Rate Limiting:
Apply rate limits at ingress (Nginx, Traefik) or application middleware
Recommended limits:
Per IP: 100 requests/minute for resume endpoints
Per token: 3 attempts before soft-block (requires investigation)
See examples/ingress-cors-snippet.md for configuration
Example: Resuming a Workflow via Bookmark
Request:
curl -X POST "https://your-elsa-server.com/elsa/api/bookmarks/resume?t=eyJhbGc..." \
-H "Content-Type: application/json" \
-d '{"approvalStatus": "approved", "comments": "LGTM"}'Expected Responses:
200 OK: Workflow resumed successfully
401 Unauthorized: Token invalid or expired
404 Not Found: Bookmark not found (already consumed or workflow cancelled)
429 Too Many Requests: Rate limit exceeded
Security Notes:
Always use HTTPS for resume URLs to prevent token interception
Validate payload schema to prevent injection attacks
Consider IP allowlisting for internal webhooks
CORS, CSRF, and Rate Limiting
CORS for Resume Endpoints
When exposing resume endpoints to browser-based applications or external systems, configure CORS carefully to prevent unauthorized access.
Configuration Example:
builder.Services.AddCors(options =>
{
options.AddPolicy("ElsaCorsPolicy", policy =>
{
policy
.WithOrigins(
"https://your-app.com",
"https://approved-partner.com"
)
.WithMethods("POST")
.WithHeaders("Content-Type", "Authorization")
.AllowCredentials(); // Only if using cookies
});
});
app.UseCors("ElsaCorsPolicy");Best Practices:
Never use
AllowAnyOrigin()in productionWhitelist only necessary origins
Prefer POST over GET for resume operations (prevents token leakage in logs)
For public webhooks, avoid CORS altogether (server-to-server only)
CSRF Considerations
Bookmark Resume Endpoints:
Resume endpoints are designed for server-to-server or webhook callbacks
If resume tokens are embedded in web pages, add CSRF protection:
builder.Services.AddAntiforgery(options => { options.HeaderName = "X-CSRF-TOKEN"; });
Studio and API:
Elsa Studio uses token-based auth (immune to CSRF)
For cookie-based sessions, enable
SameSite=StrictorSameSite=Lax
Rate Limiting
Application-Level:
using AspNetCoreRateLimit;
builder.Services.AddMemoryCache();
builder.Services.Configure<IpRateLimitOptions>(options =>
{
options.GeneralRules = new List<RateLimitRule>
{
new RateLimitRule
{
Endpoint = "POST:/elsa/api/bookmarks/resume",
Period = "1m",
Limit = 100
}
};
});
builder.Services.AddInMemoryRateLimiting();
app.UseIpRateLimiting();Ingress-Level (Preferred):
See examples/ingress-cors-snippet.md for Nginx/Traefik examples.
Ingress rate limiting is more scalable and protects against DDoS before traffic reaches the application.
Secrets Management
Storing Sensitive Configuration
Do:
Store API keys, database connections, and JWT signing keys in:
Environment variables
Azure Key Vault
AWS Secrets Manager
HashiCorp Vault
Encrypt secrets at rest in configuration stores
Rotate credentials regularly (quarterly minimum)
Don't:
Commit secrets to Git (use
.gitignoreand secret scanning)Log sensitive data in plaintext
Share API keys via email or chat
Example: Loading Secrets from Environment Variables:
builder.Configuration.AddEnvironmentVariables(prefix: "ELSA_");# Set environment variables
export ELSA_Identity__Providers__0__Options__ApiKeys__0__Key="your-secure-key-here"
export ELSA_ConnectionStrings__DefaultConnection="Server=db;Database=elsa;..."Workflow Variable Security
Sensitive Workflow Data:
Mark sensitive workflow variables as "secret" (not logged or displayed)
Avoid passing PII through workflow variables when possible
Use encrypted storage for workflow instance data containing secrets
Logging Best Practices:
Scrub PII and secrets from structured logs
Use safe fields only: WorkflowInstanceId, ActivityType, Status
Example log configuration:
{ "Logging": { "LogLevel": { "Elsa": "Information" }, "SafeFields": ["WorkflowInstanceId", "ActivityType", "Status"], "RedactedFields": ["Input", "Output", "Variables"] } }
Network & TLS
TLS Requirements
Minimum Requirements:
TLS 1.2 or higher for all HTTP endpoints
Valid, trusted certificates (not self-signed in production)
HTTPS redirect enforced:
app.UseHttpsRedirection(); app.UseHsts();
Certificate Configuration:
Obtain certificates from Let's Encrypt, DigiCert, or your organization's CA
Configure certificate renewal automation
Use wildcard certificates for multi-subdomain deployments
Mutual TLS (mTLS)
For service-to-service communication in zero-trust environments:
builder.Services.AddHttpClient<IWorkflowClient>(client =>
{
client.BaseAddress = new Uri("https://other-service.internal");
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
ClientCertificates = { LoadClientCertificate() }
});Use Cases:
Internal microservices mesh
Sensitive data workflows
Compliance requirements (PCI-DSS, HIPAA)
Session Affinity (Sticky Sessions)
Important Note: Sticky sessions are not required for Elsa workflow runtime. Workflows use distributed locking and database persistence, making them resilient to node switching mid-execution.
When Sticky Sessions May Help:
Studio UI interactions only (for caching workflow definitions client-side)
Not needed for API calls or runtime operations
See Clustering Guide (DOC-015) for more on distributed runtime architecture.
Studio Security Notes
Deploying Studio Behind Ingress
Ingress Configuration:
# Example Kubernetes Ingress (YAML)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: elsa-studio
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
tls:
- hosts:
- studio.your-domain.com
secretName: elsa-studio-tls
rules:
- host: studio.your-domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: elsa-studio
port:
number: 80SSO/Identity Integration
Studio Authentication:
Configure Studio to use the same OIDC provider as Elsa Server
Example Studio configuration:
builder.Services.AddElsaStudio(studio => { studio.UseIdentity(identity => { identity.UseOidcProvider(oidc => { oidc.Authority = "https://your-idp.com"; oidc.ClientId = "elsa-studio"; oidc.ResponseType = "code"; oidc.Scope = "openid profile elsa_api"; }); }); });
Authorization:
Implement role-based access control (RBAC) for Studio users
Restrict workflow editing to authorized roles:
[Authorize(Roles = "WorkflowAdmin")] public class WorkflowDefinitionsController : ControllerBase { }
Session Affinity for Studio
While not required for runtime, Studio benefits from session affinity to:
Cache workflow definitions in browser memory
Reduce API round-trips during editing
Configuration (Optional):
Enable sticky sessions at load balancer for Studio endpoints only
Use cookie-based or IP-hash affinity
Not needed for
/elsa/api/endpoints (runtime operations)
Production Hardening Checklist
Use this checklist to verify your Elsa deployment is production-ready from a security perspective.
Identity & Authentication
Network Security
CORS & CSRF
Resume Tokens
Secrets Management
Observability & Logging
Clustering & Distributed Runtime
Authorization
Infrastructure
Observability & Monitoring
Security-Relevant Metrics
Monitor these metrics for security anomalies:
Authentication Failures: Spike indicates brute-force attempts
Resume Token Errors: High rate of 401/404 may indicate token enumeration
Rate Limit Hits: Track which IPs/tokens are rate-limited
Workflow Cancellations: Unusual patterns may indicate attacks
Tracing with Elsa.OpenTelemetry
Enable distributed tracing to track security-relevant events:
Reference: elsa-extensions/src/modules/diagnostics/Elsa.OpenTelemetry/*
builder.Services.AddElsa(elsa =>
{
elsa.UseOpenTelemetry(otel =>
{
otel.AddElsaActivitySource();
otel.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("http://jaeger:4317");
});
});
});Security Event Tracing:
Authentication attempts (success/failure)
Resume token validation
Workflow instance modifications
Permission checks
For full monitoring setup, see the Monitoring Guide (DOC-016 - to be created).
Troubleshooting Security Issues
Common Issues and Solutions
Token Invalid or Expired
Symptom: Resume requests return 401 Unauthorized
Diagnosis:
Check token expiration: decode JWT or bookmark token
Verify clock synchronization across nodes (use NTP)
Confirm token signing key matches between issue and validation
Fix:
Adjust token TTL if expiring too quickly
Ensure all nodes use same signing key
Regenerate token if signing key rotated
CORS Blocked
Symptom: Browser console shows CORS error, API call fails
Diagnosis:
Check browser DevTools Network tab for preflight (OPTIONS) request
Verify response headers include
Access-Control-Allow-OriginConfirm origin is in whitelist
Fix:
// Add origin to CORS policy
policy.WithOrigins("https://new-app.com");Rate Limit Exceeded
Symptom: Resume requests return 429 Too Many Requests
Diagnosis:
Check rate limit configuration in ingress or middleware
Identify source IP in logs
Determine if legitimate spike or attack
Fix:
Whitelist trusted IPs if legitimate traffic
Adjust rate limits based on actual usage patterns
Implement token bucket algorithm for burst tolerance
For more troubleshooting guidance, see Troubleshooting Guide (DOC-017).
Related Documentation
Authentication & Authorization Guide - Detailed OIDC and API key setup
Clustering Guide (DOC-015) - Distributed runtime security
Troubleshooting Guide (DOC-017) - Diagnosing security issues
Monitoring Guide (DOC-016 - to be created) - Security metrics and alerting
Workflow Patterns Guide (DOC-018) - Secure workflow design
Diagram Placeholders
Note: Diagrams to be added in future updates. Suggested diagrams:
Identity flow: Client → Elsa API → OIDC Provider
Resume token lifecycle: Generation → Storage → Validation → Expiration
Network architecture: Ingress → Load Balancer → Elsa Nodes → Database
Studio SSO flow: Browser → Studio → OIDC Provider → Elsa API
Acceptance Criteria (DOC-020)
This guide meets the following acceptance criteria:
✅ Actionable, grounded guidance with copy-pasteable snippets
✅ Clear coverage of tokenized bookmark URLs (GenerateBookmarkTriggerUrl)
✅ Identity setup in Program.cs (UseIdentity, UseDefaultAuthentication)
✅ Production hardening checklist included
✅ References list with exact source file paths (see README-REFERENCES.md)
✅ SUMMARY.md updated with Security guide link
✅ Documentation-only changes; no code changes to elsa-core
Last Updated: 2025-11-25 Document ID: DOC-020
Last updated