Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http component with enabled CORS doesnt handle all http methods due to bug in aiohttp.cors lib #40513

Closed
tjunussov opened this issue Sep 23, 2020 · 9 comments

Comments

@tjunussov
Copy link

The problem

Noticed strange behaviour, when making XHR POST request from different domain ex: helloworld.com to homeassistant.local/api/hassio/ingress/session browser throws error

Access to XMLHttpRequest at 'http://homeassistant.local/api/hassio/ingress/session' from origin 'http://helloworld.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Before that request, browser sends preflight OPTION request, which has in response following CORS headers :

access-control-allow-headers: AUTHORIZATION
access-control-allow-methods: POST
access-control-allow-origin: http://helloworld.com

after that browser make real POST request to http://homeassistant.local/api/hassio/ingress/session,
but no CORS headers is presented in response :(, WHY ?

All other requests to api/auth with CORS setting specified work as expected!

Environment

  • Home Assistant Core release with the issue: Home Assistant 0.109.4
  • Last working Home Assistant Core release (if known): Supervisor 220
  • Operating environment (OS/Container/Supervised/Core): Ubuntu
  • Integration causing this issue: Hassio Ingress
  • Link to integration documentation on our website:

Problem-relevant configuration.yaml

configuration.yaml

http:
  server_port: 80
  use_x_forwarded_for: true
  trusted_proxies: 
    - 10.64.0.1
  cors_allowed_origins:
     - http://helloworld.com

Traceback/Error logs

Additional information

Options preflight request
image

POST request
image

@tjunussov
Copy link
Author

Same error for URL homeassistant.local/auth/providers, it doesnt respect cors_allowed_origins in http integration settings

@dshokouhi dshokouhi added the http label Sep 24, 2020
@tjunussov
Copy link
Author

@robbiet480 could you comment this ?

@robbiet480
Copy link
Member

Sorry, I implemented CORS years ago and it's been rewritten multiple times since. Won't be much help here.

@tjunussov
Copy link
Author

tjunussov commented Sep 27, 2020

Found in http.py file following lines

 if path.startswith("/api/hassio_ingress/"):
            return

more http.py code

def _allow_cors(route, config=None):
        """Allow CORS on a route."""
        if hasattr(route, "resource"):
            path = route.resource
        else:
            path = route

        if not isinstance(path, VALID_CORS_TYPES):
            return

        path = path.canonical

        if path.startswith("/api/hassio_ingress/"):
            return

        if path in cors_added:
            return

        cors.add(route, config)
        cors_added.add(path)

    app["allow_cors"] = lambda route: 

so it is intentionally skipping CORS headers ? why ?

@tjunussov
Copy link
Author

Confirmed, if remove this line if path.startswith("/api/hassio_ingress/"): ingress obeys CORS rules, and headers are present,

Last problem is, INGRESS uses SESSION COOKIE, no Authorization Header, so we need to provide cookie to different domain

@tjunussov
Copy link
Author

1) CORS passing credentials done! ( Session Cookie for Addons )
by adding ( allow_credentials=True )

app["allow_cors"] = lambda route: _allow_cors(
        route,
        {
            "*": aiohttp_cors.ResourceOptions(
                allow_credentials=True, allow_headers=ALLOWED_CORS_HEADERS, allow_methods="*"
            )
        },
    )

2) BUG still persists while making POST to /api/hassio/ingress/session
I think it might be in aiohttp.cors lib bug

p.s strange thing is OPTION header works fine with CORS headers included, but when making POST request it skipping CORS headers

3) /api/hassio_ingress и /api/hassio/{path} ingress/session
are served by different handlers

@tjunussov
Copy link
Author

tjunussov commented Sep 29, 2020

Finally solved CORS issue

Due to bug in aiohttp-cors lib , as workaround offered in discussion thread following changes are made:

  1. Added allow_credentials=True to allow pass Ingress_session cookie for Addons
  2. Commented out skipping from CORS hassio_ingress path
  3. As Workaround changed from app.router.resources() to app.router.routes() see this issue
  4. Changed cors_added set to match not only "path" but "path"+"method"

Full code here

cors = aiohttp_cors.setup(
        app,
        defaults={
            host: aiohttp_cors.ResourceOptions(
                allow_credentials=True, allow_headers=ALLOWED_CORS_HEADERS, allow_methods="*"
            )
            for host in origins
        },
    )

    cors_added = set()

    def _allow_cors(route, config=None):
        """Allow CORS on a route."""
        if route.method == "OPTIONS":
           return

        if hasattr(route, "resource"):
            path = route.resource.canonical
            
            if not isinstance(route.resource, VALID_CORS_TYPES):
              return
        else:
            path = "#"

        # Allow Hassio Addons CORS also
        #if path.startswith("/api/hassio_ingress/"):
        #    return

        path = f"{route.method}{path}"

        if path in cors_added:
            return

        cors.add(route, config)
        cors_added.add(path)

    app["allow_cors"] = lambda route: _allow_cors(
        route,
        {
            "*": aiohttp_cors.ResourceOptions(
                allow_credentials=True, allow_headers=ALLOWED_CORS_HEADERS, allow_methods="*"
            )
        },
    )

    if not origins:
        return

    async def cors_startup(app):
        """Initialize CORS when app starts up."""
        for route in list(app.router.routes()): # <-- Workaround
            _allow_cors(route)

@tjunussov tjunussov changed the title http CORS headers not present when making call to /api/hassio/ingress/session http component with enabled CORS doesnt handle all http methods due to bug in aiohttp.cors lib Sep 30, 2020
tjunussov added a commit to tjunussov/core that referenced this issue Sep 30, 2020
Discussion of that topic here home-assistant#40513
Due to bug in aiohttp-cor issue 155, we need use router.routes rather than router.resources, so all http methods will be with CORS
@tjunussov
Copy link
Author

Power of open source community @balloob we can close issue, if this workaround will be made

@github-actions
Copy link

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.
Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍
This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants