TandoorRecipes / recipes

Application for managing recipes, planning meals, building shopping lists and much much more!
https://docs.tandoor.dev
Other
5.44k stars 576 forks source link

CORS Errors on API calls #2180

Closed labsnoir closed 1 year ago

labsnoir commented 1 year ago

Issue

Setting: Tandoor installed on a Raspberry Pi with Docker. I want to get the name of today's meal for a dashboard. The authorization is done via Bearer Token. It works fine in Postman but not in an angular browser app.

"Access to XMLHttpRequest at 'http://<ip-to-raspberry-pi>/api/meal-plan/?from_date=2022-11-20&to_date=2022-11-20' from origin 'http://localhost:4200' 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."

Firefox shows me error 403 and sends me to the following page, but to me it is not helpful: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSPreflightDidNotSucceed

The request on the angular frontend side looks like:

      http.get<any>(this.tandoorGetMealPlanUrl,
        {headers:
          {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${this.bearerToken}`,
            'Access-Control-Allow-Methods': 'GET, OPTIONS',
            'Access-Control-Allow-Origin': '*',
            'Accept': '*/*',
            'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
          }
        })),

Any ideas what is wrong with my setup?

Tandoor Version

1.4.4

OS Version

Raspberry Pi OS / Debian 11 (Bullseye)

Setup

Docker / Docker-Compose

Reverse Proxy

No reverse proxy

Other

No response

Environment file

DEBUG=0
SQL_DEBUG=0
ALLOWED_HOSTS=*
SECRET_KEY=xyz
TIMEZONE=Europe/Berlin

DB_ENGINE=django.db.backends.postgresql
POSTGRES_HOST=db_recipes
POSTGRES_PORT=5432
POSTGRES_USER=xyz
POSTGRES_PASSWORD=xyz
POSTGRES_DB=djangodb

FRACTION_PREF_DEFAULT=0
COMMENT_PREF_DEFAULT=1
SHOPPING_MIN_AUTOSYNC_INTERVAL=5
GUNICORN_MEDIA=0
REVERSE_PROXY_AUTH=0

Docker-Compose file

version: "3"
services:
  db_recipes:
    restart: always
    image: postgres:11-alpine
    volumes:
      - ./postgresql:/var/lib/postgresql/data
    env_file:
      - ./.env

  web_recipes:
    restart: always
    image: vabene1111/recipes
    env_file:
      - ./.env
    volumes:
      - staticfiles:/opt/recipes/staticfiles
      - nginx_config:/opt/recipes/nginx/conf.d
      - ./mediafiles:/opt/recipes/mediafiles
    depends_on:
      - db_recipes

  nginx_recipes:
    image: nginx:mainline-alpine
    restart: always
    ports:
      - 80:80
    env_file:
      - ./.env
    depends_on:
      - web_recipes
    volumes:
      - nginx_config:/etc/nginx/conf.d:ro
      - staticfiles:/static:ro
      - ./mediafiles:/media:ro

volumes:
  nginx_config:
  staticfiles:

Relevant logs

No response

schrnz commented 1 year ago

@labsnoir I think there might be a few misunderstandings concerning what CORS does and how it works:

CORS is a mechanism to support the same origin policy of browsers, it controls who can see responses. This mechanism is used to avoid, e.g., malicious sites sending requests (that the browser automatically attaches session cookies to) that can leak information or act on the user's behalf without them knowing (CSRF for example). It essentially works on an allow-list approach, i.e., everything is denied until it is explicitly allowed (or * is set as an allow-all option).

Looking at your concrete situation, we first need to understand if the request is same-origin or cross-origin:

Now, the server needs to tell the browser what are allowed origins to receive the data. You can consider it like this: whom do the server devs trust to receive authenticated content besides their own origin? IF you want your example to work, the server needs to allow http://localhost:4200 and say so via the Access-Control-Allow-Origin header. (Whether you should do that for localhost is another discussion, I'm just sticking with your example here).

I see in your request that you want to set the Access-Control-Allow-Origin on the client side, but the Server needs to respond with it. If clients could just set this header for themselves, everybody could add themselves to the allow list and the mechanism would be completely broken.

So if you want to make your concrete example work, I suggest to add CSRF_TRUSTED_ORIGINS="http://localhost:4200" to your environment variables that are passed to the tandoor container. See this line in settings.py reads and parses the environment variable to be used by django.

labsnoir commented 1 year ago

Thank you for the clarification! You are right, perhaps it was not the best idea...