Skip to content

Authentication

Janno Jaerv edited this page May 5, 2026 · 1 revision

Authentication

The full picture of which token flows where. There are several distinct auth contexts in this stack; getting them mixed up is a common source of confusion.

The actors

   ┌─────────────────┐       ┌──────────────────────────┐
   │   AI agent      │──────▶│   Medusa backend         │
   │  (UCP/ACP req)  │       │   + agentic-commerce     │
   └─────────────────┘       │     plugin               │
                             └─────────────┬────────────┘
                                           │
                              ┌────────────┼────────────┐
                              │            │            │
                              ▼            ▼            ▼
                         ┌────────┐  ┌────────┐  ┌──────────┐
                         │ Prism  │  │ Stripe │  │  Other   │
                         │ Gateway│  │ Connect│  │ payment  │
                         │        │  │  ...   │  │ handlers │
                         └────────┘  └────────┘  └──────────┘

Three distinct auth boundaries to understand.

1. Agent → Medusa backend

How agents authenticate (or don't) when they hit the plugin's routes.

UCP — open by default

UCP is meant for crawl-style discovery. No auth on /.well-known/ucp or /ucp/* routes by default. Agents announce themselves with two recommended headers (per UCP spec):

UCP-Agent: <agent-name>/<version>
Request-Id: <uuid>

These are informational — used for logging and request correlation, not auth.

ACP — agentic_commerce_api_key (spec-mandated)

ACP's OpenAPI explicitly defines Bearer auth on every endpoint:

security:
  - bearerAuth: []

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key   # the spec's name for the token
  parameters:
    Authorization:
      name: Authorization
      in: header
      required: true

When agentic_commerce_api_key (the api_key plugin option, sourced from AGENTIC_COMMERCE_API_KEY env in Quick Start) is configured, all /acp/* routes require:

Authorization: Bearer <agentic_commerce_api_key>

The spec leaves token format and issuance up to the seller — the plugin accepts whatever string you set. It's a single shared secret per Medusa instance, not per-agent.

⚠️ Conformance note: api_key is currently treated as optional at the plugin level — when unset, ACP routes serve unauthenticated requests. That's non-conforming with ACP (the Authorization parameter is required: true in the spec). Useful for dev/testing; always set it for any real ACP-spec-compliant deployment.

2. Plugin → Medusa modules (internal DI)

The plugin reads from Medusa's standard modules — cart, product, order, payment, region, etc. — using the request-scoped DI container. No tokens involved; this is in-process module resolution via container.resolve("cart") etc.

The plugin doesn't reach into Medusa's database directly or use Medusa's internal admin APIs. Everything goes through the public module services so module isolation works as designed.

3. Plugin → Payment handlers

Each handler authenticates to its upstream service independently. For Prism: X-API-Key: <PRISM_API_KEY> header. For a hypothetical Stripe handler: Authorization: Bearer <STRIPE_API_KEY>.

These secrets come from the handler's options block in medusa-config.ts:

{
  resolve: "@financedistrict/medusa-plugin-prism-payment/modules/prism-payment-handler",
  options: {
    api_url: process.env.PRISM_API_URL,
    api_key: process.env.PRISM_API_KEY,    // ← the handler reads this
  },
}

Convention is for handlers to also fall back to process.env.<HANDLER>_API_KEY when no explicit option is passed (defense in depth) — see Build a Handler.

Token-secrecy summary

Token Where it lives Sensitivity
AGENTIC_COMMERCE_API_KEY Medusa env / secrets backend Medium — agent-side ACP gate
Per-handler API keys (PRISM_API_KEY, etc.) Medusa env, passed to handler module options High — direct payment-provider access

Common mistakes

  • Forgetting Authorization: Bearer ... on ACP requests. Returns 401. The agent must include the same AGENTIC_COMMERCE_API_KEY value as the Bearer token.
  • Confusing the agentic_commerce_api_key with handler API keys. The agentic_commerce_api_key is for agent → Medusa auth. Handler keys are for Medusa → upstream-service auth. Different boundaries, different tokens.
  • Putting PRISM_API_KEY in the plugin's api_key option. Wrong place. The plugin's api_key is the ACP Bearer token; Prism's API key goes in the prism-payment-handler module's api_key option.
  • Expecting per-agent keys. The current ACP setup is one shared secret per Medusa instance. Per-agent issuance is out of spec scope and not implemented.

See also

Clone this wiki locally