medusajs / medusa

The world's most flexible commerce platform.
https://medusajs.com
MIT License
25.98k stars 2.61k forks source link

Custom middleware throws CORS error when method is 'POST' #9536

Open nykkos144 opened 1 month ago

nykkos144 commented 1 month ago

What Medusa version and documentation are you using?

v1

Preliminary Checks

Issue Summary

Middleware code:

const stockLocationMiddleware = async (
  req: MedusaRequest, 
  res: MedusaResponse, 
  next: MedusaNextFunction
) => {

  next();
}

export const config: MiddlewaresConfig = {
  routes: [
    {
      matcher: '/admin/stock-locations*',
      method: 'POST',
      middlewares: [json(), stockLocationMiddleware],
    },
  ],
}

medusa-config.js:

const dotenv = require("dotenv");

let ENV_FILE_NAME = "";
switch (process.env.NODE_ENV) {
  case "production":
    ENV_FILE_NAME = ".env.production";
    break;
  case "staging":
    ENV_FILE_NAME = ".env.staging";
    break;
  case "test":
    ENV_FILE_NAME = ".env.test";
    break;
  case "development":
  default:
    ENV_FILE_NAME = ".env";
    break;
}

try {
  dotenv.config({ path: process.cwd() + "/" + ENV_FILE_NAME });
} catch (e) {}

// CORS when consuming Medusa from admin
const ADMIN_CORS = process.env.ADMIN_CORS || 
  "http://localhost:7000,http://localhost:7001"

// CORS to avoid issues when consuming Medusa from a client
const STORE_CORS = 
  process.env.STORE_CORS || "http://localhost:8000"

const DATABASE_URL =
  process.env.DATABASE_URL || "postgres://localhost/medusa-starter-default";

const REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379";

const plugins = [
  `medusa-fulfillment-manual`,
  `medusa-payment-manual`,
  {
    resolve: `@medusajs/file-local`,
    options: {
      upload_dir: "uploads",
    },
  },
  {
    resolve: "@medusajs/admin",
    /** @type {import('@medusajs/admin').PluginOptions} */
    options: {
      autoRebuild: true,
      develop: {
        open: process.env.OPEN_BROWSER !== "false",
      },
    },
  },
];

const modules = {
  inventoryService: {
    resolve: "@medusajs/inventory",
  },
  stockLocationService: {
    resolve: "@medusajs/stock-location",
  },
};

/** @type {import('@medusajs/medusa').ConfigModule["projectConfig"]} */
const projectConfig = {
  jwt_secret: process.env.JWT_SECRET || "supersecret",
  cookie_secret: process.env.COOKIE_SECRET || "supersecret",
  store_cors: STORE_CORS,
  database_url: DATABASE_URL,
  admin_cors: ADMIN_CORS,
  // Uncomment the following lines to enable REDIS
  // redis_url: REDIS_URL
};

/** @type {import('@medusajs/medusa').ConfigModule} */
module.exports = {
  projectConfig,
  plugins,
  modules,
};

When I update stock location details and /admin/stock-locations/{id} gets called I get this CORS error:

Access to XMLHttpRequest at 'http://localhost:9000/admin/stock-locations/sloc_01J945SEPXGGZ941B91FBGKHKD' from origin 'http://localhost:7001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

The error does not appear when the method property is not defined. In this situation the middleware runs correctly until I throw a MedusaError and I again get the CORS error.

How can this issue be resolved?

1. 2. 3. ...

Are you interested in working on this issue?

djordje41 commented 1 month ago

For some reason, I remember I had to add the same middleware for OPTIONS method as well. Here is the example of one of my middlewares:

{
    method: ["OPTIONS"],
    matcher: "/admin/products/:id",
    middlewares: [cors(adminCorsOptions)],
},
{
    method: ["POST"],
    matcher: "/admin/products/:id",
    middlewares: [cors(adminCorsOptions), bodyParser.json(), adminUpdateProductMiddleware],
},

Maybe you can try this. Also, maybe this is not the best solution for the problem you described, but maybe it can help until the right solution is found. Note: this was done in some earlier versions of medusa, so it might not be necessary anymore.

nykkos144 commented 1 month ago

For some reason, I remember I had to add the same middleware for OPTIONS method as well. Here is the example of one of my middlewares:

{
    method: ["OPTIONS"],
    matcher: "/admin/products/:id",
    middlewares: [cors(adminCorsOptions)],
},
{
    method: ["POST"],
    matcher: "/admin/products/:id",
    middlewares: [cors(adminCorsOptions), bodyParser.json(), adminUpdateProductMiddleware],
},

Maybe you can try this. Also, maybe this is not the best solution for the problem you described, but maybe it can help until the right solution is found. Note: this was done in some earlier versions of medusa, so it might not be necessary anymore.

Could you specify what adminCorsOptions contains?

djordje41 commented 1 month ago

For some reason, I remember I had to add the same middleware for OPTIONS method as well. Here is the example of one of my middlewares:

{
    method: ["OPTIONS"],
    matcher: "/admin/products/:id",
    middlewares: [cors(adminCorsOptions)],
},
{
    method: ["POST"],
    matcher: "/admin/products/:id",
    middlewares: [cors(adminCorsOptions), bodyParser.json(), adminUpdateProductMiddleware],
},

Maybe you can try this. Also, maybe this is not the best solution for the problem you described, but maybe it can help until the right solution is found. Note: this was done in some earlier versions of medusa, so it might not be necessary anymore.

Could you specify what adminCorsOptions contains?

Sure, adminCorsOptions is variable defined locally, some information is imported from medusa-config.js:

import {projectConfig} from "../../medusa-config";

const adminCorsOptions = {
    origin: projectConfig.admin_cors.split(","),
    credentials: true,
};