medusajs / medusa

Building blocks for digital commerce
https://medusajs.com
MIT License
24.62k stars 2.42k forks source link

/admin/users/me unauthorized, after login (via NIGINX proxy server) #8578

Open alexander-bozung opened 1 month ago

alexander-bozung commented 1 month ago

Bug report

Describe the bug

I try to setup medusa v2 in server mode with admin on the same instance. This works fine as long as I use localhost. As soon as I use an nginx proxy server in order to host the medusa server (and admin) instance with a domain I get request errors when trying to login into the admin panel under https://mydomain.xy/app:

 `GET http://localhost:9000/admin/users/me 401 (Unauthorized) errors.`

The user credentials are correct (I can login when I use localhost with the same db).

When the app is deployed locally (docker setup with 4 containers: node (the app), postgres db, redis, nginx) it is possible to login via http://localhost:9000/app/ directly but not when using the NGINX proxy server with a domain (https://server.medusajs.loc/app/).

It seems like the login is working though. In the network request I can see I get a token and a session is created, but finally the route "/admin/users/me" is called and is responding with "unauthorized" (see screenshots).

System information

Medusa version (including plugins): preview / v2 (npx create-medusa-app@preview) Node.js version: 20.16 Database: postgres Operating system: Tested on Docker (Node) and DigitalOcean (Native and with pm2) behind NGINX proxy. Browser (if relevant): -

NGINX vhost config:

server {
    listen 80;
    server_name server.medusajs.loc;
    client_max_body_size 25M;

    location / {
        # PROXY
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host:443;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_cache_bypass $http_upgrade;

        proxy_read_timeout 1m;
        proxy_connect_timeout 1m;

        # CORS
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';

        # preflight requests
        if ($request_method = 'OPTIONS') {
            return 204;
        }

        # proxy pass to the container named medusajs-node
        proxy_pass http://medusajs-node:9000; 
    }

    access_log off;
        error_log  /var/log/nginx/error.log error;
}

server {
    listen 443 ssl;
    server_name server.medusajs.loc;
    client_max_body_size 25M;

    location / {
        # PROXY
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host:443;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_cache_bypass $http_upgrade;

        proxy_read_timeout 1m;
        proxy_connect_timeout 1m;

        # CORS
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';

        # preflight requests
        if ($request_method = 'OPTIONS') {
            return 204;
        }

        # proxy pass to the container named medusajs-node
        proxy_pass http://medusajs-node:9000;
    }

    ssl_certificate /etc/nginx/certs/server.medusajs.loc.pem;
    ssl_certificate_key /etc/nginx/certs/server.medusajs.loc-key.pem;
}

Screenshots

Bildschirmfoto 2024-08-13 um 11 33 51 Bildschirmfoto 2024-08-13 um 11 35 19 Bildschirmfoto 2024-08-13 um 11 36 30 Bildschirmfoto 2024-08-13 um 11 37 53 Bildschirmfoto 2024-08-13 um 11 38 06

Deroswent commented 1 month ago

the same situation, but on localhost and for customers...

I received token from http://localhost:9000/auth/customer/emailpass and then try to receive any customer info like /store/customers/me using this token in Headers Authorization: Bearer TOKEN All requests received 401 Unauthorized. Latest medusa v2 preview

chasie commented 1 month ago

@alexander-bozung , @Deroswent can you add your medusa-config.js file

420coupe commented 4 weeks ago

I was able to get V2 working with nginx proxy manager, below is the nginx config file.

map $scheme $hsts_header {
    https   "max-age=63072000; preload";
}

server {
  set $forward_scheme http;
  set $server         "my.server.ip";
  set $port           whateverport youre running medusa on;

  listen 80;
listen [::]:80;

listen 443 ssl http2;
listen [::]:443 ssl http2;

  server_name dev.custondomain.com;

  # Let's Encrypt SSL
  include conf.d/include/letsencrypt-acme-challenge.conf;
  include conf.d/include/ssl-ciphers.conf;
  ssl_certificate /etc/letsencrypt/live/npm-20/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/npm-20/privkey.pem;

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_http_version 1.1;
  access_log /data/logs/proxy-host-22_access.log proxy;
  error_log /data/logs/proxy-host-22_error.log warn;

  location / {
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_http_version 1.1;
    # Proxy!
    include conf.d/include/proxy.conf;
  }
  # Custom
  include /data/nginx/custom/server_proxy[.]conf;
}

You have to make sure you also adjust the .env file with new domains for the CORS pieces.

srindom commented 4 weeks ago

Since the token and session endpoints succeed my guess would be that this happens because the cookie (which is set in POST /auth/session) doesn't reach the server in the subsequent requests.

Can you verify that 1) the cookie is set, and 2) that it is passed to NGINX and forwarded to the application?

alexander-bozung commented 4 weeks ago

@chasie the medusa-config.js is:

import {loadEnv, defineConfig, Modules} from '@medusajs/utils'

loadEnv(process.env.NODE_ENV, process.cwd())

module.exports = defineConfig({
    projectConfig: {
        databaseUrl: process.env.DATABASE_URL,
        redisUrl: process.env.REDIS_URL,
        http: {
            storeCors: process.env.STORE_CORS,
            adminCors: process.env.ADMIN_CORS,
            authCors: process.env.AUTH_CORS,
            jwtSecret: process.env.JWT_SECRET || "supersecret",
            cookieSecret: process.env.COOKIE_SECRET || "supersecret",
        },
        workerMode: process.env.MEDUSA_WORKER_MODE,
        admin: {
            disable: process.env.DISABLE_MEDUSA_ADMIN === "true",
            backendUrl: process.env.MEDUSA_BACKEND_URL,
            path: process.env.MEDUSA_ADMIN_PATH,
        },
    },
    modules: {
        [Modules.CACHE]: {
            resolve: "@medusajs/cache-redis",
            options: {
                redisUrl: process.env.REDIS_URL,
            },
        },
        [Modules.EVENT_BUS]: {
            resolve: "@medusajs/event-bus-redis",
            options: {
                redisUrl: process.env.REDIS_URL,
            },
        },
        [Modules.WORKFLOW_ENGINE]: {
            resolve: "@medusajs/workflow-engine-redis",
            options: {
                redis: {
                    url: process.env.REDIS_URL,
                },
            },
        },
    }
})

This is my .env file:

NODE_ENV=development

## CORS
# STOREFRONT URL
STORE_CORS=/^https?:\/\/.+$/

# ADMIN URL
ADMIN_CORS=/^https?:\/\/.+$/

# STOREFRONT AND ADMIN URLS, SEPARATED BY COMMAS
AUTH_CORS=/^https?:\/\/.+$/

## ADMIN
# for local medusa admin
MEDUSA_BACKEND_URL=https://server.medusajs.loc
MEDUSA_ADMIN_PATH=/app
DISABLE_MEDUSA_ADMIN=false

## MODE
MEDUSA_WORKER_MODE=server

## SECURITY
JWT_SECRET=***********
COOKIE_SECRET=***********

# node/npm/yarn config
NPM_CONFIG_PRODUCTION=false
YARN_PRODUCTION=false

## DB
# public network redis
REDIS_URL=redis://***********:6379

# public network postgresql
DATABASE_URL=postgres://***********
DB_HOST=***********

# postgresql
DB_USERNAME=***********
DB_PASSWORD=***********
DB_DATABASE=***********
DB_PORT=5432

@srindom A cookie is set for the given domain server.medusajs.loc. The SameSite value of the cookie is "Strict", CrossSite is empty: image

As far as I can see, the cookie connect.sid is part of the Request Headers when I try to access /app/oders after I have tried to login: image

alexander-bozung commented 2 weeks ago

anyone?