本文へ移動
cccskills
無料GitHub で公開

adobe-known-pitfalls

Identify and avoid Adobe-specific anti-patterns: using deprecated JWT auth, not caching IMS tokens, ignoring Firefly content policy, missing async job polling, and leaking p8_ secrets. Real code examples with fixes. Trigger with phrases like "adobe mistakes", "adobe anti-patterns", "adobe pitfalls", "adobe what not to do", "adobe code review".

インストール方法を見る

含まれるファイル(2)

  • SKILL.md10.4 KB
  • source.json894 B

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Adobe Known Pitfalls

Overview

The 10 most common mistakes when integrating with Adobe APIs, based on real production issues. Each pitfall includes the anti-pattern, why it fails, and the correct approach.

Prerequisites

  • Access to your Adobe integration codebase
  • Understanding of Adobe API architecture (OAuth, async jobs, rate limits)

Instructions

Pitfall 1: Still Using JWT (Service Account) Credentials

Status: CRITICAL — JWT credentials reached End of Life June 2025.

// WRONG: JWT auth (no longer works as of 2025)
import jwt from 'jsonwebtoken';
import fs from 'fs';

const privateKey = fs.readFileSync('private.key');
const jwtToken = jwt.sign({
  exp: Math.round(Date.now() / 1000) + 86400,
  iss: orgId,
  sub: technicalAccountId,
  aud: `https://ims-na1.adobelogin.com/c/${clientId}`,
}, privateKey, { algorithm: 'RS256' });

// RIGHT: OAuth Server-to-Server (current standard)
const res = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    client_id: process.env.ADOBE_CLIENT_ID!,
    client_secret: process.env.ADOBE_CLIENT_SECRET!,
    grant_type: 'client_credentials',
    scope: process.env.ADOBE_SCOPES!,
  }),
});

Pitfall 2: Not Caching IMS Access Tokens

IMS tokens are valid for 24 hours. Generating a new token per request wastes 200-500ms:

// WRONG: New token every request (200-500ms overhead each time)
async function callFirefly(prompt: string) {
  const tokenRes = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', { ... });
  const { access_token } = await tokenRes.json();
  // ... use access_token
}

// RIGHT: Cache token with expiry check
let cached: { token: string; expiresAt: number } | null = null;

async function getToken(): Promise<string> {
  if (cached && cached.expiresAt > Date.now() + 300_000) return cached.token;
  const res = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', { ... });
  const data = await res.json();
  cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
  return cached.token;
}

Pitfall 3: Using Firefly Sync Endpoint for Batch Operations

// WRONG: Sequential sync calls (each blocks 5-20s)
for (const prompt of prompts) {
  const result = await fetch('https://firefly-api.adobe.io/v3/images/generate', {
    method: 'POST', ...
  });
  results.push(await result.json());
}
// Total time: N * 5-20s = very slow

// RIGHT: Async endpoint with parallel submission
const jobs = await Promise.all(
  prompts.map(prompt =>
    fetch('https://firefly-api.adobe.io/v3/images/generate-async', {
      method: 'POST', ...
    }).then(r => r.json())
  )
);
// Poll all jobs in parallel
const results = await Promise.all(jobs.map(j => pollJob(j.statusUrl)));
// Total time: max(5-20s) = much faster

Pitfall 4: Ignoring Firefly Content Policy Errors

// WRONG: Treat all 400 errors the same
try {
  const result = await generateImage({ prompt: 'Photo of Nike shoes' });
} catch (e) {
  console.log('Generation failed');  // No idea why
}

// RIGHT: Handle content policy specifically
try {
  const result = await generateImage({ prompt });
} catch (e: any) {
  if (e.status === 400 && e.message?.includes('content policy')) {
    // Save the credit — don't retry, fix the prompt
    throw new Error(
      'Firefly content policy violation. ' +
      'Remove trademarks, real people, or explicit content from prompt.'
    );
  }
  throw e; // Other errors might be retryable
}

Pitfall 5: Uploading Files Directly to Photoshop/Lightroom API

// WRONG: Trying to upload file directly (not supported)
const formData = new FormData();
formData.append('image', fs.readFileSync('photo.jpg'));
await fetch('https://image.adobe.io/v2/remove-background', {
  method: 'POST',
  body: formData,  // Photoshop API doesn't accept direct uploads
});

// RIGHT: Use pre-signed cloud storage URLs
const inputUrl = await s3.getSignedUrl('getObject', {
  Bucket: 'my-bucket', Key: 'photo.jpg', Expires: 3600,
});
const outputUrl = await s3.getSignedUrl('putObject', {
  Bucket: 'my-bucket', Key: 'output.png', Expires: 3600,
});

await fetch('https://image.adobe.io/v2/remove-background', {
  method: 'POST',
  headers: { Authorization: `Bearer ${token}`, 'x-api-key': clientId, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    input: { href: inputUrl, storage: 'external' },
    output: { href: outputUrl, storage: 'external', type: 'image/png' },
  }),
});

Pitfall 6: Not Polling Async Job Status

Photoshop and Lightroom APIs return immediately with a job ID. You must poll for results:

// WRONG: Treating response as the final result
const res = await fetch('https://image.adobe.io/v2/remove-background', { ... });
const result = await res.json();
console.log('Done!', result);  // result is just { _links: { self: { href: ... } } }

// RIGHT: Poll the status URL until completion
const submission = await res.json();
let job;
do {
  await new Promise(r => setTimeout(r, 2000));
  const pollRes = await fetch(submission._links.self.href, {
    headers: { Authorization: `Bearer ${token}`, 'x-api-key': clientId },
  });
  job = await pollRes.json();
} while (job.status !== 'succeeded' && job.status !== 'failed');

if (job.status === 'failed') throw new Error(job.error?.message);

Pitfall 7: Leaking Adobe Credentials in Source Code

// WRONG: Hardcoded credentials (Adobe OAuth secrets start with p8_)
const client_secret = 'p8_XYZ_your_actual_secret_here_do_not_do_this';

// WRONG: Committed .env file
// git add .env && git commit -m "add config"

// RIGHT: Environment variables + .gitignore
const client_secret = process.env.ADOBE_CLIENT_SECRET!;
// .gitignore includes: .env, .env.local, .env.*.local

Pitfall 8: Not Handling PDF Services Quota

// WRONG: No quota awareness (free tier = 500 tx/month)
async function extractAllPdfs(paths: string[]) {
  for (const path of paths) {
    await extractPdf(path);  // Silently fails after 500th call
  }
}

// RIGHT: Track and enforce quota
let txCount = 0;
async function trackedExtract(path: string) {
  if (txCount >= 490) {  // Leave buffer
    throw new Error('Approaching PDF Services monthly limit. 10 transactions remaining.');
  }
  const result = await extractPdf(path);
  txCount++;
  return result;
}

Pitfall 9: Using Deprecated Photoshop Endpoints

// WRONG: v1 endpoint (deprecated)
await fetch('https://image.adobe.io/sensei/cutout', { ... });

// RIGHT: v2 endpoint (current)
await fetch('https://image.adobe.io/v2/remove-background', { ... });

Pitfall 10: Missing Webhook Signature Verification

// WRONG: Trust any incoming request (attackers can forge events)
app.post('/webhooks/adobe', (req, res) => {
  processEvent(req.body);
  res.sendStatus(200);
});

// RIGHT: Verify RSA-SHA256 signature from Adobe I/O Events
app.post('/webhooks/adobe', express.raw({ type: 'application/json' }), async (req, res) => {
  // Adobe uses RSA-SHA256 digital signatures (NOT HMAC)
  const sig = req.headers['x-adobe-digital-signature-1'];
  const keyPath = req.headers['x-adobe-public-key1-path'];

  const publicKey = await fetch(`https://static.adobeioevents.com${keyPath}`).then(r => r.text());
  const verifier = crypto.createVerify('RSA-SHA256');
  verifier.update(req.body);

  if (!verifier.verify(publicKey, sig, 'base64')) {
    return res.sendStatus(401);
  }

  processEvent(JSON.parse(req.body.toString()));
  res.sendStatus(200);
});

Quick Pitfall Scanner

# Run against your codebase
echo "=== Adobe Pitfall Scan ==="

# 1. JWT credentials (deprecated)
grep -rn "jsonwebtoken\|jwt\.sign\|RS256" --include="*.ts" --include="*.js" src/ && echo "FOUND: JWT auth (deprecated)" || echo "OK: No JWT"

# 2. Uncached token generation
grep -rn "ims/token/v3" --include="*.ts" src/ | wc -l | xargs -I{} echo "Token endpoint calls: {} (should be 1 — in auth.ts only)"

# 3. Hardcoded secrets
grep -rn "p8_" --include="*.ts" --include="*.js" src/ && echo "FOUND: Hardcoded Adobe secret" || echo "OK: No hardcoded secrets"

# 4. Deprecated endpoints
grep -rn "sensei/cutout" --include="*.ts" src/ && echo "FOUND: Deprecated Photoshop endpoint" || echo "OK: No deprecated endpoints"

# 5. Missing webhook verification
grep -rn "webhooks/adobe" --include="*.ts" src/ | grep -v "digital-signature\|verify\|RSA" && echo "WARNING: Webhook handler may lack signature verification"

Quick Reference Card

PitfallRiskDetectionFix
JWT authBroken authGrep for jwt.signMigrate to OAuth S2S
No token cachePerf (-500ms/req)Multiple ims/token callsCache with expiry
Sync Firefly for batchSlow (N*20s)Sequential generate callsUse async endpoint
Ignore content policyWasted creditsCatch 400 without reasonPre-screen prompts
Direct file upload400 errorsFormData to PhotoshopPre-signed URLs
No job pollingMissing resultsNo poll loop after submitPoll _links.self
Leaked p8_ secretCredential compromiseGrep for p8_Env vars + .gitignore
No quota trackingSilent failuresNo counterTrack per-month usage
Old PS endpoint404 errors/sensei/cutout/v2/remove-background
No webhook verifySecurity holeNo signature checkRSA-SHA256 verification

Resources

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

007

無料

Security audit, hardening, threat modeling (STRIDE/PASTA), Red/Blue Team, OWASP checks, code review, incident response, and infrastructure security for any project.

日本語の概要は準備中です。原文の説明を表示しています。

aibot88/sec_skill_store42026年5月27日 更新

Guides the creation of agile user stories and Gherkin feature files. Use when the user wants to create a user story, write acceptance criteria, define Gherkin scenarios, or author BDD feature files. This should trigger for requests such as Create a user story; Write a user story; I need to write a user story. Part of cursor-rules-java project

日本語の概要は準備中です。原文の説明を表示しています。

aibot88/sec_skill_store42026年5月27日 更新

Facilitates conversational discovery to create Architectural Decision Records (ADRs) for non-functional requirements using the ISO/IEC 25010:2023 quality model. Use when the user wants to document quality attributes, NFR decisions, security/performance/scalability architecture, or design systems with measurable quality criteria. This should trigger for requests such as Create ADR for Non-functional requirements; Document Non-functional requirements; Capture Non-functional requirements; Generate Non-functional requirements in an ADR. Part of cursor-rules-java project

日本語の概要は準備中です。原文の説明を表示しています。

aibot88/sec_skill_store42026年5月27日 更新

Run a health check on an existing project: dependency audit, security scan, test runner detection, CI/CD evaluation, and missing configuration analysis. Maps the three execution gates (pre/in/post) from /10x-bootstrapper to an assessment framework for existing codebases. Reads optional context/foundation/stack-assessment.md from /10x-stack-assess to focus checks on identified gaps. Writes context/foundation/health-check.md with findings, prioritized fixes, and an agent-readiness verdict. Use when the user has an existing project and wants to verify its health before working with an agent. Trigger phrases: "health check", "check my project", "audit my project", "is my project healthy", "sprawdź projekt", "audyt projektu", "health-check", "project health". Use AFTER /10x-stack-assess (brownfield chain), BEFORE agent onboarding (m1-l4).

日本語の概要は準備中です。原文の説明を表示しています。

aibot88/sec_skill_store42026年5月27日 更新

10x-team

無料

You MUST use this when building projects end-to-end. Orchestrates all 12 team roles — automatically switches between CTO, architect, PM, engineers, SRE, security, DBA, QA, and EM based on the current phase of work. Starts with brainstorming before any implementation.

日本語の概要は準備中です。原文の説明を表示しています。

aibot88/sec_skill_store42026年5月27日 更新

Use when you need to add or configure Maven plugins in your pom.xml — including quality tools (enforcer, surefire, failsafe, jacoco, pitest, spotbugs, pmd), security scanning (OWASP), code formatting (Spotless), version management, container image build (Jib), build information tracking, and benchmarking (JMH) — through a consultative, modular step-by-step approach that only adds what you actually need. This should trigger for requests such as Add Maven plugins in pom.xml; Improve Maven plugins in pom.xml. Part of cursor-rules-java project

日本語の概要は準備中です。原文の説明を表示しています。

aibot88/sec_skill_store42026年5月27日 更新

aibot88 のスキルをすべて見る

このスキルの問題を報告する