Restrict Order Retrieval
In this guide, you'll learn how Medusa handles access to the Get an Order API route, and how to restrict access to it in your Medusa application.
How Medusa Handles Order Retrieval#
The GET /store/orders/:id API route doesn't require customer authentication. Any request that includes a valid publishable API key and a correct order ID receives the order's details.
Medusa applies this behavior intentionally. Guest customers place orders without an account, so they have no session or token to authenticate with. After they complete the cart, the storefront redirects them to an order confirmation page that retrieves the order by its ID. Requiring authentication would break that page.
The order's ID acts as the credential in this flow. Medusa generates order IDs randomly, so guessing an ID requires brute forcing a value from an address space large enough to make the attempt impractical.
Restrict Access to the Route#
If your store doesn't allow guest checkout, you may want stricter access rules. You can add access rules with middlewares. Middlewares that you apply to an existing API route run in addition to the route's original middlewares, so you don't have to replicate the route.
For example, add the authenticate middleware to the route:
A request without an authenticated customer now receives a 401 error, while a logged-in customer still retrieves the order.
Restrict the Route to the Order's Customer#
To allow only the customer that placed the order to retrieve it, add a custom middleware that compares the authenticated customer's ID to the order's customer_id.
Create the file src/api/middlewares/ensure-order-owner.ts with the following content:
1import {2 AuthenticatedMedusaRequest,3 MedusaNextFunction,4 MedusaResponse,5} from "@medusajs/framework/http"6import {7 ContainerRegistrationKeys,8 MedusaError,9} from "@medusajs/framework/utils"10 11export async function ensureOrderOwner(12 req: AuthenticatedMedusaRequest,13 res: MedusaResponse,14 next: MedusaNextFunction15) {16 const query = req.scope.resolve(17 ContainerRegistrationKeys.QUERY18 )19 20 const { data: [order] } = await query.graph({21 entity: "order",22 fields: ["id", "customer_id"],23 filters: {24 id: req.params.id,25 },26 })27 28 if (order?.customer_id !== req.auth_context.actor_id) {29 return next(30 new MedusaError(31 MedusaError.Types.UNAUTHORIZED,32 "You're not allowed to retrieve this order."33 )34 )35 }36 37 next()38}
The middleware retrieves the order's customer_id with Query. The auth_context.actor_id property holds the ID of the customer that the authenticate middleware authenticated. If the two IDs don't match, the middleware rejects the request with a 401 error.
Then, apply the middleware after the authenticate middleware:
1import {2 defineMiddlewares,3 authenticate,4} from "@medusajs/framework/http"5import {6 ensureOrderOwner,7} from "./middlewares/ensure-order-owner"8 9export default defineMiddlewares({10 routes: [11 {12 matcher: "/store/orders/:id",13 method: ["GET"],14 middlewares: [15 authenticate("customer", ["session", "bearer"]),16 ensureOrderOwner,17 ],18 },19 ],20})
The order of the middlewares matters. The authenticate middleware must run first, since ensureOrderOwner reads the customer that it authenticated.
Now, only the customer that placed the order can retrieve it. Other logged-in customers receive a 401 error.