Wardn Hub
MCP ServersSkillsCategoriesAPI docsSubmit server
Submit server
Wardn HubTrusted MCP server directory.

Registry

  • MCP Servers
  • Skills
  • Categories

Resources

  • API docs
  • Score method

Contribute

  • Submit server
  • Advertise
© 2026 Wardn Hub
Wardn Hub
MCP ServersSkillsCategoriesAPI docsSubmit server
Submit server
skills/jeremylongshore/claude-code-plugins-plus-skills/curated-canva-multi-env-setup

curated-canva-multi-env-setup

1
jeremylongshore/claude-code-plugins-plus-skills·Other Tools and Integrations·Audit pending·Snapshot 9d6c3eb6a9f4

Summary

This source did not publish a separate summary. Review SKILL.md before using the skill.

SKILL.md

Canva Multi-Environment Setup

Overview

Configure Canva Connect API integrations across development, staging, and production. Each environment needs separate OAuth integrations registered in the Canva developer portal with distinct redirect URIs.

Environment Strategy

EnvironmentCanva IntegrationRedirect URIData
Developmentmy-app-devhttp://localhost:3000/auth/canva/callbackTest account
Stagingmy-app-staginghttps://staging.myapp.com/auth/canva/callbackStaging account
Productionmy-app-prodhttps://myapp.com/auth/canva/callbackReal users

Important: Register a separate Canva integration per environment. Each gets its own client ID and secret.

Configuration

// src/config/canva.ts
interface CanvaEnvConfig {
  clientId: string;
  clientSecret: string;
  redirectUri: string;
  baseUrl: string;  // Always api.canva.com — Canva has no sandbox API
  scopes: string[];
  debug: boolean;
}

const configs: Record<string, CanvaEnvConfig> = {
  development: {
    clientId: process.env.CANVA_CLIENT_ID!,
    clientSecret: process.env.CANVA_CLIENT_SECRET!,
    redirectUri: 'http://localhost:3000/auth/canva/callback',
    baseUrl: 'https://api.canva.com/rest/v1', // No sandbox exists
    scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],
    debug: true,
  },
  staging: {
    clientId: process.env.CANVA_CLIENT_ID!,
    clientSecret: process.env.CANVA_CLIENT_SECRET!,
    redirectUri: process.env.CANVA_REDIRECT_URI!,
    baseUrl: 'https://api.canva.com/rest/v1',
    scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],
    debug: false,
  },
  production: {
    clientId: process.env.CANVA_CLIENT_ID!,
    clientSecret: process.env.CANVA_CLIENT_SECRET!,
    redirectUri: process.env.CANVA_REDIRECT_URI!,
    baseUrl: 'https://api.canva.com/rest/v1',
    scopes: ['design:content:write', 'design:content:read', 'design:meta:read'],
    debug: false,
  },
};

export function getCanvaConfig(): CanvaEnvConfig {
  const env = process.env.NODE_ENV || 'development';
  return configs[env] || configs.development;
}

Secret Management

Local Development

# .env.local (git-ignored)
CANVA_CLIENT_ID=OCA_dev_xxxxxxxx
CANVA_CLIENT_SECRET=dev_xxxxxxxx

GitHub Actions / CI

# Per-environment secrets
gh secret set CANVA_CLIENT_ID --env staging --body "OCA_staging_xxx"
gh secret set CANVA_CLIENT_SECRET --env staging --body "staging_xxx"
gh secret set CANVA_CLIENT_ID --env production --body "OCA_prod_xxx"
gh secret set CANVA_CLIENT_SECRET --env production --body "prod_xxx"

Production — Cloud Secret Managers

# GCP Secret Manager
gcloud secrets create canva-client-id-prod --data-file=-
gcloud secrets create canva-client-secret-prod --data-file=-

# AWS Secrets Manager
aws secretsmanager create-secret \
  --name canva/production/client-id \
  --secret-string "OCA_prod_xxx"

# HashiCorp Vault
vault kv put secret/canva/production \
  client_id="OCA_prod_xxx" \
  client_secret="prod_xxx"

Environment Isolation Guards

// Prevent accidental cross-environment operations
function assertEnvironment(expected: string): void {
  const actual = process.env.NODE_ENV || 'development';
  if (actual !== expected) {
    throw new Error(`Expected ${expected} environment, got ${actual}`);
  }
}

// Guard destructive operations
async function deleteAllUserDesigns(userId: string, token: string) {
  assertEnvironment('development'); // Block in staging/production
  // ...
}

Token Storage per Environment

// Development: file-based for convenience
// Staging/Production: encrypted database

function getTokenStore(): TokenStore {
  const env = process.env.NODE_ENV || 'development';

  if (env === 'development') {
    return new FileTokenStore('.canva-tokens.json'); // git-ignored
  }

  return new DatabaseTokenStore({
    connectionString: process.env.DATABASE_URL!,
    encryptionKey: process.env.TOKEN_ENCRYPTION_KEY!,
  });
}

Canva-Specific Considerations

  1. No sandbox API — Canva has no separate sandbox environment. All environments hit api.canva.com/rest/v1. Use separate Canva accounts for dev/staging.
  2. Separate integrations — Each environment should be a distinct integration in the Canva developer portal to avoid redirect URI conflicts.
  3. Scope differences — Use broader scopes in dev for testing, minimal scopes in production.
  4. Token isolation — Never share tokens across environments. Refresh tokens are single-use.

Error Handling

IssueCauseSolution
Wrong redirect URIEnvironment mismatchUse per-environment integration
Missing secretNot deployed to envAdd via secret manager
Token cross-contaminationShared token storeIsolate by environment prefix
Production guard triggeredWrong NODE_ENVSet correct environment variable

Resources

  • Canva Creating Integrations
  • Canva Authentication
  • 12-Factor App Config

Next Steps

For observability setup, see canva-observability.

Related skills

implementing-backup-strategieskubernetes-secrets-managerbuilding-gitops-workflowsmanaging-api-cachemanaging-network-policies