--- name: webflow-security-basics description: "Apply Webflow API security best practices \u2014 token management, scope\ \ least privilege,\nOAuth 2.0 secret rotation, webhook signature verification, and\ \ audit logging.\nUse when securing API tokens, implementing least privilege access,\n\ or auditing Webflow security configuration.\nTrigger with phrases like \"webflow\ \ security\", \"webflow secrets\",\n\"secure webflow\", \"webflow API key security\"\ , \"webflow token rotation\".\n" allowed-tools: Read, Write, Edit, Grep version: 1.0.0 license: MIT author: Jeremy Longshore tags: - saas - design - no-code - webflow compatibility: Designed for Claude Code --- # Webflow Security Basics ## Overview Security best practices for Webflow Data API v2 tokens, OAuth secrets, webhook verification, and access control. Covers the full lifecycle from token creation to rotation and revocation. ## Prerequisites - Webflow developer account at `developers.webflow.com` - Understanding of environment variables - Secret management solution (vault, cloud secret manager, etc.) ## Instructions ### Step 1: Token Types and Selection | Token Type | Scope | Best For | |------------|-------|----------| | **Workspace Token** | All sites in workspace | Internal tools, scripts | | **Site Token** | Single site only | Single-site integrations | | **OAuth Access Token** | User-authorized scopes | Public apps, marketplace apps | **Rule: Never use a workspace token where a site token would suffice.** ### Step 2: Least Privilege Scopes Only request scopes your integration actually needs: | Operation | Minimum Scope | |-----------|--------------| | Read site info | `sites:read` | | Publish site | `sites:write` | | Read CMS content | `cms:read` | | Create/update CMS items | `cms:write` | | Read pages | `pages:read` | | Read form submissions | `forms:read` | | Read products/orders | `ecommerce:read` | | Create products, fulfill orders | `ecommerce:write` | ```typescript // Example: Read-only integration needs only these scopes const READ_ONLY_SCOPES = "sites:read cms:read pages:read forms:read"; // CMS sync integration const CMS_SYNC_SCOPES = "sites:read cms:read cms:write"; // Full ecommerce integration const ECOMMERCE_SCOPES = "sites:read ecommerce:read ecommerce:write"; ``` ### Step 3: Secure Token Storage ```bash # .gitignore — MANDATORY .env .env.local .env.*.local *.pem *.key # .env.local (never committed) WEBFLOW_API_TOKEN=your-token-here WEBFLOW_WEBHOOK_SECRET=your-webhook-secret WEBFLOW_OAUTH_CLIENT_SECRET=your-oauth-secret ``` ```typescript // Load from environment, never hardcode import { WebflowClient } from "webflow-api"; function createClient(): WebflowClient { const token = process.env.WEBFLOW_API_TOKEN; if (!token) { throw new Error( "WEBFLOW_API_TOKEN not set. " + "Generate at https://developers.webflow.com" ); } return new WebflowClient({ accessToken: token }); } ``` ### Step 4: Secret Rotation Procedure ```bash # 1. Generate new token at developers.webflow.com # (old token remains valid until revoked) # 2. Update secret in your deployment platform # Vercel vercel env rm WEBFLOW_API_TOKEN production vercel env add WEBFLOW_API_TOKEN production # Fly.io fly secrets set WEBFLOW_API_TOKEN=new-token-here # GCP Secret Manager echo -n "new-token-here" | \ gcloud secrets versions add webflow-api-token --data-file=- # AWS Secrets Manager aws secretsmanager update-secret \ --secret-id webflow/api-token \ --secret-string "new-token-here" # 3. Verify new token works curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $NEW_TOKEN" \ https://api.webflow.com/v2/sites # 4. Revoke old token in Webflow dashboard # 5. Monitor for 401 errors in logs ``` ### Step 5: Environment-Specific Keys ```typescript // Use different tokens per environment const TOKEN_MAP: Record = { development: "WEBFLOW_API_TOKEN_DEV", staging: "WEBFLOW_API_TOKEN_STAGING", production: "WEBFLOW_API_TOKEN_PROD", }; function getToken(): string { const env = process.env.NODE_ENV || "development"; const envVar = TOKEN_MAP[env] || TOKEN_MAP.development; const token = process.env[envVar]; if (!token) throw new Error(`${envVar} not set for ${env} environment`); return token; } ``` ### Step 6: Webhook Signature Verification Webflow webhooks include a signature header for request verification: ```typescript import crypto from "crypto"; function verifyWebhookSignature( rawBody: Buffer | string, signature: string, secret: string ): boolean { if (!signature || !secret) return false; const expectedSignature = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); // Timing-safe comparison prevents timing attacks try { return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } catch { return false; // Length mismatch } } // Express middleware import express from "express"; app.post( "/webhooks/webflow", express.raw({ type: "application/json" }), (req, res) => { const signature = req.headers["x-webflow-signature"] as string; const secret = process.env.WEBFLOW_WEBHOOK_SECRET!; if (!verifyWebhookSignature(req.body, signature, secret)) { console.error("Webhook signature verification failed"); return res.status(401).json({ error: "Invalid signature" }); } const event = JSON.parse(req.body.toString()); // Process verified event... res.status(200).json({ received: true }); } ); ``` ### Step 7: Audit Logging ```typescript interface WebflowAuditEntry { timestamp: string; operation: string; method: string; endpoint: string; statusCode: number; tokenType: "workspace" | "site" | "oauth"; environment: string; durationMs: number; } function logApiCall(entry: WebflowAuditEntry): void { // Structured JSON log — ship to your logging platform console.log(JSON.stringify({ level: entry.statusCode >= 400 ? "error" : "info", service: "webflow-integration", ...entry, })); } ``` ## Security Checklist - [ ] API tokens stored in environment variables, never in code - [ ] `.env` files in `.gitignore` - [ ] Different tokens for dev/staging/production - [ ] Minimal scopes per token (least privilege) - [ ] Webhook signatures verified on every request - [ ] Timing-safe comparison for signature checks - [ ] Token rotation procedure documented and tested - [ ] Audit logging enabled for all API calls - [ ] No tokens in client-side code (tokens are server-side only) - [ ] Git history scanned for accidentally committed tokens ## Output - Secure token storage with environment isolation - Least privilege scope selection - Webhook signature verification - Token rotation procedure - Audit logging for compliance ## Error Handling | Security Issue | Detection | Mitigation | |---------------|-----------|------------| | Token in git history | `git log -p \| grep WEBFLOW` | Revoke token immediately, rotate | | Excessive scopes | Review at developers.webflow.com | Generate new token with minimal scopes | | Missing webhook verification | No signature check in code | Add `verifyWebhookSignature()` | | Token never rotated | No rotation log | Schedule quarterly rotation | ## Resources - [Webflow Authentication](https://developers.webflow.com/data/reference/authentication) - [Webflow Scopes](https://developers.webflow.com/data/reference/scopes) - [OAuth Reference](https://developers.webflow.com/data/reference/oauth-app) ## Next Steps For production deployment, see `webflow-prod-checklist`.