Skip to content

Commit 22796a6

Browse files
authored
docs: add guide on restricting access for get order route (#16343)
* docs: add guide on restricting access for get order route * Update page.mdx
1 parent c0f2670 commit 22796a6

12 files changed

Lines changed: 204 additions & 3 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
---
2+
tags:
3+
- order
4+
- auth
5+
- name: how to
6+
label: Restrict Order Retrieval
7+
- server
8+
products:
9+
- order
10+
- auth
11+
---
12+
13+
export const metadata = {
14+
title: `Restrict Order Retrieval`,
15+
}
16+
17+
# {metadata.title}
18+
19+
In this guide, you'll learn how Medusa handles access to the [Get an Order API route](!api!/store/orders/get-an-order), and how to restrict access to it in your Medusa application.
20+
21+
## How Medusa Handles Order Retrieval
22+
23+
The `GET /store/orders/:id` API route doesn't require customer authentication. Any request that includes a valid [publishable API key](../../../storefront-development/publishable-api-keys/page.mdx) and a correct order ID receives the order's details.
24+
25+
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.
26+
27+
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.
28+
29+
<Note>
30+
31+
The [List Orders API route](!api!/store/orders/list-orders), which lists a customer's orders, does require customer authentication. Only the retrieval route accepts unauthenticated requests.
32+
33+
</Note>
34+
35+
---
36+
37+
## Restrict Access to the Route
38+
39+
If your store doesn't allow guest checkout, you may want stricter access rules. You can add access rules with [middlewares](!docs!/learn/fundamentals/api-routes/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.
40+
41+
For example, add the [authenticate middleware](!docs!/learn/fundamentals/api-routes/protected-routes#protect-custom-api-routes) to the route:
42+
43+
```ts title="src/api/middlewares.ts"
44+
import {
45+
defineMiddlewares,
46+
authenticate,
47+
} from "@medusajs/framework/http"
48+
49+
export default defineMiddlewares({
50+
routes: [
51+
{
52+
matcher: "/store/orders/:id",
53+
method: ["GET"],
54+
middlewares: [
55+
authenticate("customer", ["session", "bearer"]),
56+
],
57+
},
58+
],
59+
})
60+
```
61+
62+
A request without an authenticated customer now receives a `401` error, while a logged-in customer still retrieves the order.
63+
64+
<Note>
65+
66+
This middleware only checks that a customer is authenticated. It doesn't check that the customer owns the order, so any logged-in customer can retrieve any order. Refer to the [next section](#restrict-the-route-to-the-orders-customer) to also check ownership.
67+
68+
</Note>
69+
70+
### Restrict the Route to the Order's Customer
71+
72+
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`.
73+
74+
Create the file `src/api/middlewares/ensure-order-owner.ts` with the following content:
75+
76+
export const ownerHighlights = [
77+
["12", "AuthenticatedMedusaRequest", "Access the authenticated customer's details."],
78+
["20", "query", "Retrieve the order's customer."],
79+
["28", "actor_id", "The ID of the authenticated customer."],
80+
]
81+
82+
```ts title="src/api/middlewares/ensure-order-owner.ts" highlights={ownerHighlights}
83+
import {
84+
AuthenticatedMedusaRequest,
85+
MedusaNextFunction,
86+
MedusaResponse,
87+
} from "@medusajs/framework/http"
88+
import {
89+
ContainerRegistrationKeys,
90+
MedusaError,
91+
} from "@medusajs/framework/utils"
92+
93+
export async function ensureOrderOwner(
94+
req: AuthenticatedMedusaRequest,
95+
res: MedusaResponse,
96+
next: MedusaNextFunction
97+
) {
98+
const query = req.scope.resolve(
99+
ContainerRegistrationKeys.QUERY
100+
)
101+
102+
const { data: [order] } = await query.graph({
103+
entity: "order",
104+
fields: ["id", "customer_id"],
105+
filters: {
106+
id: req.params.id,
107+
},
108+
})
109+
110+
if (order?.customer_id !== req.auth_context.actor_id) {
111+
return next(
112+
new MedusaError(
113+
MedusaError.Types.UNAUTHORIZED,
114+
"You're not allowed to retrieve this order."
115+
)
116+
)
117+
}
118+
119+
next()
120+
}
121+
```
122+
123+
The middleware retrieves the order's `customer_id` with [Query](!docs!/learn/fundamentals/module-links/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.
124+
125+
Then, apply the middleware after the `authenticate` middleware:
126+
127+
```ts title="src/api/middlewares.ts" highlights={[["16", "ensureOrderOwner", "Run the ownership check after authentication."]]}
128+
import {
129+
defineMiddlewares,
130+
authenticate,
131+
} from "@medusajs/framework/http"
132+
import {
133+
ensureOrderOwner,
134+
} from "./middlewares/ensure-order-owner"
135+
136+
export default defineMiddlewares({
137+
routes: [
138+
{
139+
matcher: "/store/orders/:id",
140+
method: ["GET"],
141+
middlewares: [
142+
authenticate("customer", ["session", "bearer"]),
143+
ensureOrderOwner,
144+
],
145+
},
146+
],
147+
})
148+
```
149+
150+
The order of the middlewares matters. The `authenticate` middleware must run first, since `ensureOrderOwner` reads the customer that it authenticated.
151+
152+
Now, only the customer that placed the order can retrieve it. Other logged-in customers receive a `401` error.

www/apps/resources/app/storefront-development/checkout/order-confirmation/page.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ In this guide, you'll learn how to show the different order details on the order
1818

1919
After the customer completes the checkout process and places an order, you can show an order confirmation page to display the order details.
2020

21+
<Note>
22+
23+
The [Get an Order API route](!api!/store/orders/get-an-order) doesn't require customer authentication, so that guest customers can view their order confirmation page. To add access rules to the route, refer to the [Restrict Order Retrieval guide](../../../commerce-modules/order/secure-order-retrieval/page.mdx).
24+
25+
</Note>
26+
2127
## Retrieve Order Details
2228

2329
To show the order details, you need to retrieve the order by sending a request to the [Get an Order API route](!api!/store/orders/get-an-order).

www/apps/resources/generated/edit-dates.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5754,7 +5754,7 @@ export const generatedEditDates = {
57545754
"references/notification_service/interfaces/notification_service.INotificationModuleService/page.mdx": "2026-04-30T16:36:44.178Z",
57555755
"app/nextjs-starter/guides/revalidate-cache/page.mdx": "2025-05-01T15:33:42.490Z",
57565756
"app/storefront-development/cart/totals/page.mdx": "2025-09-15T15:13:56.268Z",
5757-
"app/storefront-development/checkout/order-confirmation/page.mdx": "2025-12-16T07:17:57.133Z",
5757+
"app/storefront-development/checkout/order-confirmation/page.mdx": "2026-08-06T07:45:07.260Z",
57585758
"app/how-to-tutorials/tutorials/product-reviews/page.mdx": "2026-01-12T12:25:29.951Z",
57595759
"app/troubleshooting/data-models/default-fields/page.mdx": "2025-03-21T06:59:06.775Z",
57605760
"app/troubleshooting/medusa-admin/blocked-request/page.mdx": "2025-03-21T06:53:34.854Z",
@@ -7643,5 +7643,6 @@ export const generatedEditDates = {
76437643
"app/lint/rules/widget-must-export-config/page.mdx": "2026-07-28T05:43:12.267Z",
76447644
"app/lint/rules/widget-must-have-default-export/page.mdx": "2026-07-28T05:43:12.274Z",
76457645
"app/lint/rules/widget-zone-must-be-string-literal/page.mdx": "2026-07-28T05:43:12.270Z",
7646-
"app/commerce-modules/cart/sales-channel-availability/page.mdx": "2026-07-31T13:14:07.601Z"
7646+
"app/commerce-modules/cart/sales-channel-availability/page.mdx": "2026-07-31T13:14:07.601Z",
7647+
"app/commerce-modules/order/secure-order-retrieval/page.mdx": "2026-08-06T07:59:42.895Z"
76477648
}

www/apps/resources/generated/files-map.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,10 @@ export const filesMap = [
383383
"filePath": "/www/apps/resources/app/commerce-modules/order/return/page.mdx",
384384
"pathname": "/commerce-modules/order/return"
385385
},
386+
{
387+
"filePath": "/www/apps/resources/app/commerce-modules/order/secure-order-retrieval/page.mdx",
388+
"pathname": "/commerce-modules/order/secure-order-retrieval"
389+
},
386390
{
387391
"filePath": "/www/apps/resources/app/commerce-modules/order/tax-lines/page.mdx",
388392
"pathname": "/commerce-modules/order/tax-lines"

www/apps/resources/generated/generated-commerce-modules-sidebar.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,13 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
501501
"path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/phone-auth",
502502
"children": []
503503
},
504+
{
505+
"loaded": true,
506+
"type": "ref",
507+
"title": "Restrict Order Retrieval",
508+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval",
509+
"children": []
510+
},
504511
{
505512
"loaded": true,
506513
"type": "link",
@@ -6243,6 +6250,13 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
62436250
"path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/re-order",
62446251
"children": []
62456252
},
6253+
{
6254+
"loaded": true,
6255+
"type": "ref",
6256+
"title": "Restrict Order Retrieval",
6257+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval",
6258+
"children": []
6259+
},
62466260
{
62476261
"loaded": true,
62486262
"type": "link",

www/apps/resources/generated/generated-how-to-tutorials-sidebar.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,13 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
181181
"path": "https://docs.medusajs.com/resources/commerce-modules/auth/reset-password",
182182
"children": []
183183
},
184+
{
185+
"loaded": true,
186+
"type": "ref",
187+
"title": "Restrict Order Retrieval",
188+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval",
189+
"children": []
190+
},
184191
{
185192
"loaded": true,
186193
"type": "ref",

www/apps/resources/generated/sitemap-urls.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export const sitemapUrls = [
9595
"/commerce-modules/order",
9696
"/commerce-modules/order/promotion-adjustments",
9797
"/commerce-modules/order/return",
98+
"/commerce-modules/order/secure-order-retrieval",
9899
"/commerce-modules/order/tax-lines",
99100
"/commerce-modules/order/transactions",
100101
"/commerce-modules/order/transfer-to-guest",

www/packages/tags/src/tags/auth.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ export const auth = [
1111
"title": "Reset Password",
1212
"path": "https://docs.medusajs.com/user-guide/reset-password"
1313
},
14+
{
15+
"title": "Restrict Order Retrieval",
16+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval"
17+
},
1418
{
1519
"title": "How to Add Custom Authentication in Medusa Admin",
1620
"path": "https://docs.medusajs.com/resources/how-to-tutorials/how-to/admin/auth"

www/packages/tags/src/tags/how-to.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export const howTo = [
2727
"title": "Retrieve Order Totals",
2828
"path": "https://docs.medusajs.com/resources/commerce-modules/order/order-totals"
2929
},
30+
{
31+
"title": "Restrict Order Retrieval",
32+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval"
33+
},
3034
{
3135
"title": "Filter Products",
3236
"path": "https://docs.medusajs.com/resources/commerce-modules/product/guides/filter-products"

www/packages/tags/src/tags/order.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export const order = [
6363
"title": "Retrieve Order Totals Using Query",
6464
"path": "https://docs.medusajs.com/resources/commerce-modules/order/order-totals"
6565
},
66+
{
67+
"title": "Restrict Order Retrieval",
68+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval"
69+
},
6670
{
6771
"title": "Implement Quote Management",
6872
"path": "https://docs.medusajs.com/resources/examples/guides/quote-management"

0 commit comments

Comments
 (0)