Skip to content

Conversation

@NicolasGorga
Copy link
Contributor

@NicolasGorga NicolasGorga commented Nov 13, 2025

Summary

What — What changes are introduced in this PR?

Resolve the user_id from the user linked to secret api key when updating a draft order with api-key auth type and set ti to created_by field of order change.

Why — Why are these changes relevant or necessary?

Currently for this scenario, we incorrectly set the secret api key id as the created_by field of the corresponding order_change which causes authentication issues when that same user tries to interact with the change.

How — How have these changes been implemented?

Check the auth type in the draft order edit endpoint and if we identify it is api-key then instead of resolving the user_id from req.auth_context.actor_id that would point to the api key id, we search for the user that created the api key and set its id.

Testing — How have these changes been tested, or how can the reviewer test the feature?

Added tests.


Examples

Provide examples or code snippets that demonstrate how this feature works, or how it can be used in practice.
This helps with documentation and ensures maintainers can quickly understand and verify the change.

// Example usage

Checklist

Please ensure the following before requesting a review:

  • I have added a changeset for this PR
    • Every non-breaking change should be marked as a patch
    • To add a changeset, run yarn changeset and follow the prompts
  • The changes are covered by relevant tests
  • I have verified the code works as intended locally
  • I have linked the related issue(s) if applicable

Additional Context

Add any additional context, related issues, or references that might help the reviewer understand this PR.

fixes #13859
closes SUP-2560


Note

Ensure draft order edits authenticated via API key use the key owner’s user_id for created_by, adding middleware to populate context and tests to verify.

  • Backend/API
    • Update POST /admin/draft-orders/:id to resolve user_id from the secret API key’s created_by when actor_type is api-key.
    • Add middleware set-secret-api-key-context and apply to "/admin*" to populate req.secret_key_context; export in framework/src/http/middlewares/index.ts and extend framework/src/http/types.ts with SecretKeyContext.
  • Tests
    • Add integration test asserting order_change.created_by matches the user linked to the secret API key.
  • Changeset
    • Patch release for @medusajs/medusa.

Written by Cursor Bugbot for commit 6a6b0b8. This will update automatically on new commits. Configure here.

@NicolasGorga NicolasGorga requested a review from a team as a code owner November 13, 2025 14:49
@changeset-bot
Copy link

changeset-bot bot commented Nov 13, 2025

🦋 Changeset detected

Latest commit: 6a6b0b8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 74 packages
Name Type
@medusajs/medusa Patch
@medusajs/test-utils Patch
@medusajs/medusa-oas-cli Patch
integration-tests-http Patch
@medusajs/analytics Patch
@medusajs/api-key Patch
@medusajs/auth Patch
@medusajs/caching Patch
@medusajs/cart Patch
@medusajs/currency Patch
@medusajs/customer Patch
@medusajs/file Patch
@medusajs/fulfillment Patch
@medusajs/index Patch
@medusajs/inventory Patch
@medusajs/link-modules Patch
@medusajs/locking Patch
@medusajs/notification Patch
@medusajs/order Patch
@medusajs/payment Patch
@medusajs/pricing Patch
@medusajs/product Patch
@medusajs/promotion Patch
@medusajs/region Patch
@medusajs/sales-channel Patch
@medusajs/settings Patch
@medusajs/stock-location Patch
@medusajs/store Patch
@medusajs/tax Patch
@medusajs/user Patch
@medusajs/workflow-engine-inmemory Patch
@medusajs/workflow-engine-redis Patch
@medusajs/draft-order Patch
@medusajs/oas-github-ci Patch
@medusajs/cache-inmemory Patch
@medusajs/cache-redis Patch
@medusajs/event-bus-local Patch
@medusajs/event-bus-redis Patch
@medusajs/analytics-local Patch
@medusajs/analytics-posthog Patch
@medusajs/auth-emailpass Patch
@medusajs/auth-github Patch
@medusajs/auth-google Patch
@medusajs/caching-redis Patch
@medusajs/file-local Patch
@medusajs/file-s3 Patch
@medusajs/fulfillment-manual Patch
@medusajs/locking-postgres Patch
@medusajs/locking-redis Patch
@medusajs/notification-local Patch
@medusajs/notification-sendgrid Patch
@medusajs/payment-stripe Patch
@medusajs/core-flows Patch
@medusajs/framework Patch
@medusajs/js-sdk Patch
@medusajs/modules-sdk Patch
@medusajs/orchestration Patch
@medusajs/types Patch
@medusajs/utils Patch
@medusajs/workflows-sdk Patch
@medusajs/cli Patch
@medusajs/deps Patch
@medusajs/telemetry Patch
@medusajs/admin-bundler Patch
@medusajs/admin-sdk Patch
@medusajs/admin-shared Patch
@medusajs/admin-vite-plugin Patch
@medusajs/dashboard Patch
@medusajs/icons Patch
@medusajs/toolbox Patch
@medusajs/ui-preset Patch
create-medusa-app Patch
medusa-dev-cli Patch
@medusajs/ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Nov 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

8 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
api-reference Ignored Ignored Nov 25, 2025 10:48am
api-reference-v2 Ignored Ignored Preview Nov 25, 2025 10:48am
cloud-docs Ignored Ignored Preview Nov 25, 2025 10:48am
docs-ui Ignored Ignored Preview Nov 25, 2025 10:48am
docs-v2 Ignored Ignored Preview Nov 25, 2025 10:48am
medusa-docs Ignored Ignored Preview Nov 25, 2025 10:48am
resources-docs Ignored Ignored Preview Nov 25, 2025 10:48am
user-guide Ignored Ignored Preview Nov 25, 2025 10:48am

Comment on lines +43 to +54
let userId = req.auth_context.actor_id
const authType = req.auth_context.actor_type as AuthType

const shouldResolveUser = authType === 'api-key'
if (shouldResolveUser) {
const {data: [apiKey]} = await query.graph({
entity: 'api_key',
fields: ['created_by'],
filters: { id: userId },
})
userId = apiKey.created_by
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: This seems like something that should live in a middleware, as this is not really specific to draft orders ... think we should consider doing that instead

Also, this code does not follow our linting conventions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed I think we could follow something similar to existent publishable_key_context for secret_key_context and add the created_by prop in the new middleware, I started with this approach. Oh yes, forgot to run the formatter, will do :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is an issue with your linter configuration ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for middleware

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed commit with middleware, applied it to all /admin routes, could you let me know:

  • if the middleware looks good to you
  • if the way i applied to all admin routes is the correct way

Comment on lines +43 to +54
let userId = req.auth_context.actor_id
const authType = req.auth_context.actor_type as AuthType

const shouldResolveUser = authType === 'api-key'
if (shouldResolveUser) {
const {data: [apiKey]} = await query.graph({
entity: 'api_key',
fields: ['created_by'],
filters: { id: userId },
})
userId = apiKey.created_by
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for middleware

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on December 17

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

const shouldSkip = req.auth_context.actor_type !== "api-key"
if (shouldSkip) {
next()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Middleware: API Key Logic Runs Unchecked

Missing return statement after calling next() when skipping non-api-key requests. This causes the middleware to continue executing the api_key query logic for all request types, not just api-key authenticated requests. Non-api-key requests will query the api_key table unnecessarily and potentially call next() twice.

Fix in Cursor Fix in Web

filters: { id: userId },
})
userId = apiKey.created_by
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Route handler duplicates API key query unnecessarily

The route handler queries the API key to resolve created_by even though the setSecretApiKeyContext middleware already sets this value in req.secret_key_context.created_by. This duplicates the database query and could cause inconsistencies since the route query lacks withDeleted: true while the middleware includes it. The route should use req.secret_key_context?.created_by instead of performing its own query.

Fix in Cursor Fix in Web

middlewares: [
setSecretApiKeyContext,
]
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Middleware breaks unauthenticated admin routes

The setSecretApiKeyContext middleware is registered for all /admin* routes but throws an error when req.auth_context is missing. Some admin routes have opted out of authentication via export const AUTHENTICATE = false (e.g., /admin/feature-flags). For these routes, the global auth middleware skips setting auth_context, causing setSecretApiKeyContext to throw "No auth_context found in request" and breaking unauthenticated admin endpoints.

Fix in Cursor Fix in Web

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Draft order edits using Secret api key

4 participants