Claude-code-plugins adobe-security-basics
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/adobe-pack/skills/adobe-security-basics" ~/.claude/skills/jeremylongshore-claude-code-plugins-adobe-security-basics && rm -rf "$T"
manifest:
plugins/saas-packs/adobe-pack/skills/adobe-security-basics/SKILL.mdsource content
Adobe Security Basics
Overview
Security best practices for Adobe OAuth Server-to-Server credentials, I/O Events webhook signature verification, and least-privilege access control across Adobe APIs.
Prerequisites
- Adobe Developer Console access
- Understanding of OAuth 2.0 client_credentials flow
- Access to secret management solution (Vault, AWS Secrets Manager, GCP Secret Manager)
Instructions
Step 1: Secure Credential Storage
# .env (NEVER commit to git) ADOBE_CLIENT_ID=abc123def456 ADOBE_CLIENT_SECRET=p8_XYZ_your_secret_here ADOBE_SCOPES=openid,AdobeID,firefly_api # .gitignore — MUST include these .env .env.local .env.*.local *.pem *.key
# Production: use your cloud provider's secret manager # AWS Secrets Manager aws secretsmanager create-secret \ --name adobe/production/credentials \ --secret-string '{"client_id":"...","client_secret":"..."}' # GCP Secret Manager echo -n "your-client-secret" | gcloud secrets create adobe-client-secret --data-file=- # HashiCorp Vault vault kv put secret/adobe/prod client_id="..." client_secret="..."
Step 2: Credential Rotation
Adobe OAuth Server-to-Server credentials support multiple client secrets simultaneously, enabling zero-downtime rotation:
# 1. In Adobe Developer Console, generate a NEW client_secret # (old secret remains valid) # 2. Update your secret manager with the new secret aws secretsmanager update-secret \ --secret-id adobe/production/credentials \ --secret-string '{"client_id":"...","client_secret":"NEW_SECRET"}' # 3. Deploy application with new secret # 4. Verify new secret works curl -X POST 'https://ims-na1.adobelogin.com/ims/token/v3' \ -d "client_id=${ADOBE_CLIENT_ID}&client_secret=${NEW_SECRET}&grant_type=client_credentials&scope=${ADOBE_SCOPES}" # 5. Delete old client_secret in Developer Console
Step 3: Least-Privilege Scope Selection
| Scope | Grants | Use When |
|---|---|---|
| Basic identity | Always required |
| Adobe identity info | Always required |
| Firefly image generation | Firefly workflows only |
| Firefly Services (Photoshop, Lightroom) | Creative API workflows |
| Org info access | Multi-tenant apps |
// Per-environment scope restriction const SCOPES_BY_ENV: Record<string, string> = { development: 'openid,AdobeID', // Minimal for testing staging: 'openid,AdobeID,firefly_api', // Only APIs being tested production: 'openid,AdobeID,firefly_api,ff_apis', // Full production access };
Step 4: I/O Events Webhook Signature Verification
Adobe I/O Events uses RSA-SHA256 digital signatures, not HMAC. The public keys are served from
static.adobeioevents.com:
// src/adobe/webhook-verify.ts import crypto from 'crypto'; interface AdobeWebhookHeaders { 'x-adobe-digital-signature-1': string; 'x-adobe-digital-signature-2': string; 'x-adobe-public-key1-path': string; 'x-adobe-public-key2-path': string; } // Cache public keys (they rotate infrequently) const publicKeyCache = new Map<string, string>(); async function getPublicKey(keyPath: string): Promise<string> { if (publicKeyCache.has(keyPath)) return publicKeyCache.get(keyPath)!; const response = await fetch(`https://static.adobeioevents.com${keyPath}`); const publicKey = await response.text(); publicKeyCache.set(keyPath, publicKey); return publicKey; } export async function verifyAdobeWebhookSignature( rawBody: Buffer, headers: Record<string, string> ): Promise<boolean> { // Try both signatures (Adobe sends two for key rotation) for (const i of [1, 2]) { const signature = headers[`x-adobe-digital-signature-${i}`]; const keyPath = headers[`x-adobe-public-key${i}-path`]; if (!signature || !keyPath) continue; try { const publicKey = await getPublicKey(keyPath); const verifier = crypto.createVerify('RSA-SHA256'); verifier.update(rawBody); if (verifier.verify(publicKey, signature, 'base64')) { return true; } } catch (err) { console.warn(`Signature ${i} verification failed:`, err); } } return false; } // Express middleware import express from 'express'; app.post('/webhooks/adobe', express.raw({ type: 'application/json' }), async (req, res) => { // Handle challenge verification (registration handshake) if (req.query.challenge) { return res.json({ challenge: req.query.challenge }); } // Verify digital signature if (!await verifyAdobeWebhookSignature(req.body, req.headers as any)) { return res.status(401).json({ error: 'Invalid signature' }); } const event = JSON.parse(req.body.toString()); await processEvent(event); res.status(200).json({ received: true }); } );
Step 5: Git Secret Scanning
# .github/workflows/secret-scan.yml name: Adobe Secret Scan on: [push, pull_request] jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Scan for Adobe credentials run: | # Client secrets start with p8_ (OAuth Server-to-Server) if grep -rE "p8_[A-Za-z0-9_-]{20,}" --include="*.ts" --include="*.js" --include="*.py" .; then echo "ERROR: Potential Adobe client secret found in source code" exit 1 fi echo "No Adobe secrets detected"
Security Checklist
- OAuth credentials in secret manager, not source code
-
files in.env.gitignore - Different credentials per environment (dev/staging/prod)
- Minimal scopes per environment
- Webhook signatures verified with RSA-SHA256
- Secret rotation procedure documented and tested
- Git secret scanning enabled in CI
- Access tokens cached (not re-generated per request)
Error Handling
| Security Issue | Detection | Mitigation |
|---|---|---|
| Exposed client_secret | Git scanning alert | Rotate in Developer Console immediately |
| Wrong scopes | error | Review product profile assignments |
| Unverified webhooks | Missing signature check | Implement RSA-SHA256 verification |
| Stale credentials | Auth failures in monitoring | Schedule periodic rotation |
Resources
Next Steps
For production deployment, see
adobe-prod-checklist.