Skip to content

feat(magento,algolia): use shared resolveSecret to decrypt CMS secrets#72

Merged
JonasJesus42 merged 1 commit into
mainfrom
JonasJesus42/resolve-secret-decrypt
Jun 3, 2026
Merged

feat(magento,algolia): use shared resolveSecret to decrypt CMS secrets#72
JonasJesus42 merged 1 commit into
mainfrom
JonasJesus42/resolve-secret-decrypt

Conversation

@JonasJesus42

@JonasJesus42 JonasJesus42 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces local resolveSecret helpers in magento/client.ts and algolia/client.ts with the shared resolveSecret from @decocms/start/sdk/crypto so encrypted CMS secrets actually decrypt at boot.

Why

Both initMagentoFromBlocks and initAlgoliaFromBlocks carried their own local resolveSecret that only read process.env[name]. Sites whose Deco CMS ships secrets as { encrypted: "<aes-cbc hex>", name: "X" } (the production default — admin AES-encrypts every Secret block with the site's DECO_CRYPTO_KEY) silently produced apiKey: "". Downstream magentoFetch.buildHeaders then skipped the Authorization: Bearer line entirely → every Magento request returned 401 Unauthorized.

This surfaced live on granadobr-tanstack as a minicart that wouldn't load. Curl reproduction:

curl -X POST https://granadobr-tanstack.deco-cx.workers.dev/deco/invoke/magento/loaders/cart \
  -H 'content-type: application/json' \
  --data-raw '{"cartId":"123abc"}'

# {"error":"[Magento] cart loader: 401 Unauthorized"}

VTEX and Shopify already accept a ResolveSecretFn parameter (see vtex/mod.ts:53, shopify/mod.ts:40) that walks the full chain:

  1. plain string — dev override
  2. { get: () => string } — legacy Secret loader
  3. { encrypted: "<hex>" } — AES-CBC decrypt via DECO_CRYPTO_KEY (prod)
  4. process.env[name] — env-var fallback

Magento and Algolia now follow the same chain via direct import from @decocms/start/sdk/crypto.

Breaking change

initMagentoFromBlocks and initAlgoliaFromBlocks are now async (Promise<void> / Promise<boolean>) because the decrypt step is async. Site setups must await the calls before any loader fires.

- initMagentoFromBlocks(blocks);
- initAlgoliaFromBlocks(blocks);
+ await initMagentoFromBlocks(blocks);
+ await initAlgoliaFromBlocks(blocks);

The only known consumer (granadobr-tanstack) gets the await in the follow-up bump commit.

Resend

resend/client.ts doesn't have an initFromBlocks helper yet — sites currently call configureResend({ apiKey: process.env.X! }) manually. Left a TODO referencing this change so the same migration ships when Resend grows a CMS-block bootstrap.

Tests

  • magento/__tests__/client.test.ts: 5 new cases — missing block, plain string, Secret + env var, no-decrypt no-env-var fallback, dual apiKey + originHeader resolution.
  • algolia/__tests__/client.test.ts: existing init tests converted to async + new case for the env-var-fallback through an encrypted branch when DECO_CRYPTO_KEY is unset.

601 tests pass on bun run test; bun run typecheck clean.

Test plan

  • bun run typecheck clean
  • bun run test — all 49 files / 601 tests pass (+ 6 new)
  • Bump @decocms/apps in granadobr-tanstack, await the inits, verify /deco/invoke/magento/loaders/cart no longer 401s, minicart loads end-to-end.

🤖 Generated with Claude Code


Summary by cubic

Magento and Algolia now use the shared resolveSecret from @decocms/start/sdk/crypto to decrypt CMS secrets at boot, fixing empty API keys and 401s. Both init helpers are now async and must be awaited.

  • Bug Fixes

    • Replaced local secret resolvers with the shared resolveSecret (string → .get() → AES-CBC decrypt via DECO_CRYPTO_KEYprocess.env), ensuring Authorization headers are set and Magento/Algolia loaders work.
    • Added tests covering plain strings, encrypted secrets, env fallbacks, and multiple fields.
  • Migration

    • Add await to initMagentoFromBlocks(...) and initAlgoliaFromBlocks(...) before any loader runs.

Written for commit af3f294. Summary will update on new commits.

Review in cubic

`initMagentoFromBlocks` and `initAlgoliaFromBlocks` each carried their
own local `resolveSecret` helper that *only* read `process.env[name]`.
For sites that ship their secrets through the production Deco CMS —
where every Secret block looks like
`{ encrypted: "<aes-cbc hex>", name: "ENV_NAME" }` — the env-var-only
helper silently produced `apiKey: ""` and the downstream `magentoFetch`
suppressed the `Authorization: Bearer` header. Result: every Magento
request returned 401 Unauthorized, which surfaced in the field as a
minicart that never loaded (cart loader threw on the auth failure).

VTEX and Shopify already accept a `ResolveSecretFn` parameter and use
the shared `resolveSecret` from `@decocms/start/sdk/crypto`, which
walks:

  1. plain string                                  (dev override)
  2. `{ get: () => string }`                       (legacy Secret object)
  3. `{ encrypted: "<hex>" }` decrypted via        (prod default)
     `DECO_CRYPTO_KEY` (AES-CBC)
  4. `process.env[name]`                           (fallback)

Magento + Algolia now follow the same chain. Both inits are async
(decryption is async) — site setups need to `await` the call before
any loader fires.

`resend/client.ts` does not have an `initFromBlocks` helper yet —
left a TODO referencing this commit so the same migration ships when
Resend grows a CMS-block bootstrap (it's currently configured via
`configureResend({ apiKey: process.env.X! })`).

Tests:
  - magento/__tests__/client.test.ts: 5 new cases covering missing
    block, plain string, Secret + env var, no-decrypt no-env-var
    fallback, and dual apiKey + originHeader resolution.
  - algolia/__tests__/client.test.ts: all existing init tests
    converted to async + new env-var-fallback case for the encrypted
    branch without DECO_CRYPTO_KEY.

Breaking change: `initMagentoFromBlocks` and `initAlgoliaFromBlocks`
now return `Promise<void>` and `Promise<boolean>` respectively. Site
setups must add `await`. Existing call sites (granadobr-tanstack)
will be updated in the bump-and-refactor follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JonasJesus42 JonasJesus42 requested a review from a team June 3, 2026 18:50
@JonasJesus42 JonasJesus42 merged commit 40d3da2 into main Jun 3, 2026
1 of 2 checks passed
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 4.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant