Skip to content

Commit 96d40d2

Browse files
authored
docs: add docs on gating returns creation from storefront (#16360)
1 parent aedd3f3 commit 96d40d2

14 files changed

Lines changed: 652 additions & 10 deletions

File tree

www/apps/book/public/llms-full.txt

Lines changed: 370 additions & 6 deletions
Large diffs are not rendered by default.

www/apps/resources/app/commerce-modules/order/return/page.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ A return is the return of items delivered from the customer back to the merchant
1818

1919
A return is requested either by the customer from the storefront, or the merchant from the admin. Medusa supports an automated Return Merchandise Authorization (RMA) flow.
2020

21+
<Note>
22+
23+
The [Create Return API route](!api!/store/returns/create-return) doesn't require customer authentication, so that guest customers can request a return for their orders. To add access rules to the route, refer to the [Restrict Return Creation guide](../secure-return-creation/page.mdx).
24+
25+
</Note>
26+
2127
![Diagram showcasing the automated RMA flow.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719578128/Medusa%20Resources/return-rma_pzprwq.jpg)
2228

2329
Once the merchant receives the returned items, they mark the return as received.
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
---
2+
tags:
3+
- order
4+
- auth
5+
- name: how to
6+
label: Restrict Return Creation
7+
- server
8+
products:
9+
- order
10+
- auth
11+
---
12+
13+
export const metadata = {
14+
title: `Restrict Return Creation`,
15+
}
16+
17+
# {metadata.title}
18+
19+
In this guide, you'll learn how Medusa handles access to the [Create Return API route](!api!/store/returns/create-return), and how to restrict access to it in your Medusa application.
20+
21+
## How Medusa Handles Return Creation
22+
23+
The `POST /store/returns` 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 creates a return for that order.
24+
25+
Medusa applies this behavior intentionally. Guest customers place orders without an account, so they have no session or token to authenticate with. They still need a way to request a return for the items they received. Requiring authentication would leave guest customers without a self-service return flow.
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+
Medusa doesn't expose a store API route to retrieve or list returns. Customers only create returns, while merchants manage them from the Medusa Admin dashboard.
32+
33+
</Note>
34+
35+
### The receive_now Request Body Parameter
36+
37+
The route accepts a `receive_now` parameter in the request body. When it's enabled, Medusa marks the return's items as received as soon as it creates the return, without waiting for the merchant to receive them.
38+
39+
This is useful in stores that trust the customer's return request, such as digital goods or low-value items. If your store reviews returns before receiving them, reject the parameter as explained in the [Reject the receive_now Parameter](#reject-the-receive_now-parameter) section.
40+
41+
---
42+
43+
## Restrict Access to the Route
44+
45+
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.
46+
47+
For example, add the [authenticate middleware](!docs!/learn/fundamentals/api-routes/protected-routes#protect-custom-api-routes) to the route:
48+
49+
```ts title="src/api/middlewares.ts"
50+
import {
51+
defineMiddlewares,
52+
authenticate,
53+
} from "@medusajs/framework/http"
54+
55+
export default defineMiddlewares({
56+
routes: [
57+
{
58+
matcher: "/store/returns",
59+
method: ["POST"],
60+
middlewares: [
61+
authenticate("customer", ["session", "bearer"]),
62+
],
63+
},
64+
],
65+
})
66+
```
67+
68+
A request without an authenticated customer now receives a `401` error, while a logged-in customer still creates the return.
69+
70+
<Note>
71+
72+
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 create a return for any order. Refer to the [next section](#restrict-the-route-to-the-orders-customer) to also check ownership.
73+
74+
</Note>
75+
76+
### Restrict the Route to the Order's Customer
77+
78+
To allow only the customer that placed the order to create a return for it, add a custom middleware that compares the authenticated customer's ID to the order's `customer_id`.
79+
80+
Create the file `src/api/middlewares/ensure-return-order-owner.ts` with the following content:
81+
82+
export const ownerHighlights = [
83+
["12", "AuthenticatedMedusaRequest", "Access the authenticated customer's details."],
84+
["20", "order_id", "Retrieve the order's ID from the request body."],
85+
["22", "query", "Retrieve the order's customer."],
86+
["30", "actor_id", "The ID of the authenticated customer."],
87+
]
88+
89+
```ts title="src/api/middlewares/ensure-return-order-owner.ts" highlights={ownerHighlights}
90+
import {
91+
AuthenticatedMedusaRequest,
92+
MedusaNextFunction,
93+
MedusaResponse,
94+
} from "@medusajs/framework/http"
95+
import {
96+
ContainerRegistrationKeys,
97+
MedusaError,
98+
} from "@medusajs/framework/utils"
99+
100+
export async function ensureReturnOrderOwner(
101+
req: AuthenticatedMedusaRequest,
102+
res: MedusaResponse,
103+
next: MedusaNextFunction
104+
) {
105+
const query = req.scope.resolve(
106+
ContainerRegistrationKeys.QUERY
107+
)
108+
109+
const { order_id } = req.body as { order_id?: string }
110+
111+
const { data: [order] } = await query.graph({
112+
entity: "order",
113+
fields: ["id", "customer_id"],
114+
filters: {
115+
id: order_id,
116+
},
117+
})
118+
119+
if (order?.customer_id !== req.auth_context.actor_id) {
120+
return next(
121+
new MedusaError(
122+
MedusaError.Types.UNAUTHORIZED,
123+
"You're not allowed to create a return for this order."
124+
)
125+
)
126+
}
127+
128+
next()
129+
}
130+
```
131+
132+
The middleware retrieves the order's ID from the request body, since the Create Return API route accepts it as a body parameter. It then 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.
133+
134+
Then, apply the middleware after the `authenticate` middleware:
135+
136+
```ts title="src/api/middlewares.ts" highlights={[["16", "ensureReturnOrderOwner", "Run the ownership check after authentication."]]}
137+
import {
138+
defineMiddlewares,
139+
authenticate,
140+
} from "@medusajs/framework/http"
141+
import {
142+
ensureReturnOrderOwner,
143+
} from "./middlewares/ensure-return-order-owner"
144+
145+
export default defineMiddlewares({
146+
routes: [
147+
{
148+
matcher: "/store/returns",
149+
method: ["POST"],
150+
middlewares: [
151+
authenticate("customer", ["session", "bearer"]),
152+
ensureReturnOrderOwner,
153+
],
154+
},
155+
],
156+
})
157+
```
158+
159+
The order of the middlewares matters. The `authenticate` middleware must run first, since `ensureReturnOrderOwner` reads the customer that it authenticated.
160+
161+
Now, only the customer that placed the order can create a return for it. Other logged-in customers receive a `401` error.
162+
163+
---
164+
165+
## Reject the receive_now Parameter
166+
167+
If your store reviews returns before marking their items as received, reject the `receive_now` parameter on the storefront. Merchants can still mark the return as received from the Medusa Admin dashboard or the Admin API.
168+
169+
Create the file `src/api/middlewares/reject-receive-now.ts` with the following content:
170+
171+
```ts title="src/api/middlewares/reject-receive-now.ts"
172+
import {
173+
MedusaNextFunction,
174+
MedusaRequest,
175+
MedusaResponse,
176+
} from "@medusajs/framework/http"
177+
import { MedusaError } from "@medusajs/framework/utils"
178+
179+
export async function rejectReceiveNow(
180+
req: MedusaRequest,
181+
res: MedusaResponse,
182+
next: MedusaNextFunction
183+
) {
184+
const { receive_now } = req.body as {
185+
receive_now?: boolean
186+
}
187+
188+
if (receive_now) {
189+
return next(
190+
new MedusaError(
191+
MedusaError.Types.NOT_ALLOWED,
192+
"You can't receive a return's items."
193+
)
194+
)
195+
}
196+
197+
next()
198+
}
199+
```
200+
201+
Then, apply the middleware to the route:
202+
203+
```ts title="src/api/middlewares.ts" highlights={[["13", "rejectReceiveNow", "Reject requests that enable receive_now."]]}
204+
import { defineMiddlewares } from "@medusajs/framework/http"
205+
import {
206+
rejectReceiveNow,
207+
} from "./middlewares/reject-receive-now"
208+
209+
export default defineMiddlewares({
210+
routes: [
211+
{
212+
matcher: "/store/returns",
213+
method: ["POST"],
214+
middlewares: [
215+
// other middlewares...
216+
rejectReceiveNow
217+
],
218+
},
219+
],
220+
})
221+
```
222+
223+
A request that enables `receive_now` now receives a `400` error. The merchant marks the return as received later, as explained in the [Order Return documentation](../return/page.mdx).

www/apps/resources/app/nextjs-starter/guides/storefront-returns/page.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ The API route accepts the following query parameters:
168168

169169
Finally, you'll add a function that sends a request to the [Create Return](!api!/store/returns/create-return) API route. This creates a return request for an order.
170170

171+
<Note>
172+
173+
The Create Return API route doesn't require customer authentication, so that guest customers can request a return for their orders. To add access rules to the route, refer to the [Restrict Return Creation guide](../../../commerce-modules/order/secure-return-creation/page.mdx).
174+
175+
</Note>
176+
171177
In the same `src/lib/data/returns.ts` file, add the following function:
172178

173179
```ts title="src/lib/data/returns.ts"

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const generatedEditDates = {
3838
"app/commerce-modules/order/exchange/page.mdx": "2025-02-26T11:23:29.845Z",
3939
"app/commerce-modules/order/order-versioning/page.mdx": "2026-04-10T07:11:40.191Z",
4040
"app/commerce-modules/order/promotion-adjustments/page.mdx": "2024-10-09T10:19:19.333Z",
41-
"app/commerce-modules/order/return/page.mdx": "2025-02-26T11:22:49.675Z",
41+
"app/commerce-modules/order/return/page.mdx": "2026-08-07T11:09:00.324Z",
4242
"app/commerce-modules/order/tax-lines/page.mdx": "2026-07-05T20:54:48.913Z",
4343
"app/commerce-modules/order/transactions/page.mdx": "2025-10-03T10:35:16.560Z",
4444
"app/commerce-modules/order/page.mdx": "2025-08-26T09:21:49.780Z",
@@ -6293,7 +6293,7 @@ export const generatedEditDates = {
62936293
"references/core_flows/Locking/Steps_Locking/variables/core_flows.Locking.Steps_Locking.releaseLockStepId/page.mdx": "2025-09-15T09:52:14.219Z",
62946294
"references/core_flows/Locking/core_flows.Locking.Steps_Locking/page.mdx": "2025-09-15T09:52:14.217Z",
62956295
"app/integrations/guides/meilisearch/page.mdx": "2025-11-27T08:21:28.779Z",
6296-
"app/nextjs-starter/guides/storefront-returns/page.mdx": "2026-01-12T12:27:41.338Z",
6296+
"app/nextjs-starter/guides/storefront-returns/page.mdx": "2026-08-07T11:08:53.480Z",
62976297
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.views/page.mdx": "2026-04-30T16:36:41.272Z",
62986298
"app/data-model-repository-reference/methods/create/page.mdx": "2025-10-28T16:02:14.959Z",
62996299
"app/data-model-repository-reference/methods/delete/page.mdx": "2025-10-28T16:02:17.380Z",
@@ -7644,5 +7644,6 @@ export const generatedEditDates = {
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",
76467646
"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"
7647+
"app/commerce-modules/order/secure-order-retrieval/page.mdx": "2026-08-06T07:59:42.895Z",
7648+
"app/commerce-modules/order/secure-return-creation/page.mdx": "2026-08-07T11:16:40.482Z"
76487649
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ export const filesMap = [
387387
"filePath": "/www/apps/resources/app/commerce-modules/order/secure-order-retrieval/page.mdx",
388388
"pathname": "/commerce-modules/order/secure-order-retrieval"
389389
},
390+
{
391+
"filePath": "/www/apps/resources/app/commerce-modules/order/secure-return-creation/page.mdx",
392+
"pathname": "/commerce-modules/order/secure-return-creation"
393+
},
390394
{
391395
"filePath": "/www/apps/resources/app/commerce-modules/order/tax-lines/page.mdx",
392396
"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
@@ -508,6 +508,13 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
508508
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval",
509509
"children": []
510510
},
511+
{
512+
"loaded": true,
513+
"type": "ref",
514+
"title": "Restrict Return Creation",
515+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-return-creation",
516+
"children": []
517+
},
511518
{
512519
"loaded": true,
513520
"type": "link",
@@ -6257,6 +6264,13 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
62576264
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval",
62586265
"children": []
62596266
},
6267+
{
6268+
"loaded": true,
6269+
"type": "ref",
6270+
"title": "Restrict Return Creation",
6271+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-return-creation",
6272+
"children": []
6273+
},
62606274
{
62616275
"loaded": true,
62626276
"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
@@ -188,6 +188,13 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
188188
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval",
189189
"children": []
190190
},
191+
{
192+
"loaded": true,
193+
"type": "ref",
194+
"title": "Restrict Return Creation",
195+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-return-creation",
196+
"children": []
197+
},
191198
{
192199
"loaded": true,
193200
"type": "ref",

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export const sitemapUrls = [
9696
"/commerce-modules/order/promotion-adjustments",
9797
"/commerce-modules/order/return",
9898
"/commerce-modules/order/secure-order-retrieval",
99+
"/commerce-modules/order/secure-return-creation",
99100
"/commerce-modules/order/tax-lines",
100101
"/commerce-modules/order/transactions",
101102
"/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
@@ -15,6 +15,10 @@ export const auth = [
1515
"title": "Restrict Order Retrieval",
1616
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-order-retrieval"
1717
},
18+
{
19+
"title": "Restrict Return Creation",
20+
"path": "https://docs.medusajs.com/resources/commerce-modules/order/secure-return-creation"
21+
},
1822
{
1923
"title": "How to Add Custom Authentication in Medusa Admin",
2024
"path": "https://docs.medusajs.com/resources/how-to-tutorials/how-to/admin/auth"

0 commit comments

Comments
 (0)