"401: Unauthorized" iframe CARD of Grafana not working

alright check it out, its a dirty hack but this how i got around this problem:

  1. create a folder called www in your homeassistant config folder, so its /homeassistant/config/www
    1a. if the folder dint exist, make sure you restart home assistant!!!
  2. create a new html file in the new www directory, anyfilename.html
    2a. the contents of anyfilename.html are:
<!DOCTYPE html>
<html>
<head>
   <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
   <meta http-equiv="Pragma" content="no-cache">
   <meta http-equiv="Expires" content="0">
</head>
<body>
  <h2>Loading..</h2>
  <p id = "result"></p>
  <script>
     redirect()
     function redirect () {
        setTimeout(myURL, 2000);
        var result = document.getElementById("result");
        result.innerHTML = ".";
     }

     function myURL() {
        const queryString = window.location.search;
        document.location.href = `/api/hassio_ingress/HASSIOingresRandomURL/${queryString}`;
     }
  </script>
</body>
</html>

change HASSIOingresRandomURL to what your ingress url isbroken 401 webpage card hasā€¦ example would be /api/hassio_ingress/whatevertokenRandomURL/d-solo/7Tl95WoGz/server-stats Also take note of the 2000 milosecond (2s) load delay.

  1. In your dashboard that contains a webpage (iframe) card that has a broken 401 /api/hassio_ingress/HASSIOingresRandomURL/ Change it to /local/anyfilename.html with arguments at the end. Example would be: /local/anyfilename.html?orgId=1&refresh=1m&from=now-1h&to=now&panelId=3

  2. we will add a dummy webpage card anywhere in the dashboard tab with the following settings:

- type: iframe
  url: /TheSideBarURL
  aspect_ratio: 0%

This creates an invisible card to force load grafana as if you clicked on it on the sidebar.

  1. Now when you load the dashboard tab, grafana is loaded by the invisible webpage card and after 2 seconds your HASSIOingresRandomURL with arguments & w/e you want are loaded in.

Until home assistant fixes passing url parameters & paths to ingressURLā€™s on the sidebar, we have to come up with crap like thisā€¦ Tested working on external URLs like CASA & Cloudflared. Anyone not logged in who accesses /local/anyfilename.html are still greeted with 401 not authorized.

You can test this faster by using desktop browser. Closing the browser completely (not just the tab) will force 401 to appear.

Lifesaver! For those banging their heads, the config line should be followed by a semicolon, like so:

proxy_hide_header X-Frame-Options;
2 Likes

Actually a better way if you have a fqdn for both Graphana and Home assistant. Remove my old command and add this instead. Itā€™s more secure, and will only allow iframe renders from the named ancestors. Vs completely disabling and allowing it to be used in an iframe anywhere.

add_header Content-Security-Policy "frame-ancestors graphana.yourdomain.com homeassistant.yourdomain.com;";
1 Like

I fixed this in Scrypted by having the add-on write a html card to config/www. The contents of that card will use an access token to create an ingress session using the web socket api, and then navigate to the ingress url after setting the ingress_session cookie.

In my example, the card is written to /config/www/scrypted_card.html and used within the Webpage frame as /local/scrypted_card.html (with access token in query string).

The access token can be found in localStorage on desktop browsers, but seemingly is unavailable in mobile apps. Thus, itā€™s best to provide the token in a query string.

<html>

<head>
    <style>
    </style>
    <script>
        const embedUrl = new URL(window.location.href);
        const cameraId = embedUrl.searchParams.get('id');

        let accessToken = embedUrl.searchParams.get('accessToken');
        if (!accessToken) {
            const hassTokens = JSON.parse(localStorage.hassTokens)
            accessToken = hassTokens.access_token;
        }

        const u = new URL('/api/websocket', window.location.href);
        const https = u.protocol === 'https';
        u.protocol = https ? 'wss' : 'ws';
        const ws = new WebSocket(u.toString());

        const wsSend = data => {
            ws.send(JSON.stringify(data));
        }

        ws.addEventListener('open', () => {
            wsSend({
                type: 'auth',
                access_token: accessToken,
            });

            wsSend({
                type: "supervisor/api",
                endpoint: "/addons",
                method: "get",
                id: 1,
            });

        });

        let ingress_url;
        ws.addEventListener('message', e => {
            const data = JSON.parse(e.data);
            if (data.id === 1) {
                const scrypted = data.result.addons.find(addon => addon.slug.endsWith('_scrypted'));
                wsSend({
                    type: "supervisor/api",
                    endpoint: `/addons/${scrypted.slug}/info`,
                    method: "get",
                    id: 2,
                });
            }
            else if (data.id === 2) {
                ingress_url = data.result.ingress_url;
                console.error(ingress_url)
                wsSend({
                    type: "supervisor/api",
                    endpoint: "/ingress/session",
                    method: "post",
                    id: 3,
                });
            }
            else if (data.id === 3) {
                const session = data?.result?.session;
                document.cookie = `ingress_session=${session}; path=/`;
                window.location.href = `${ingress_url}endpoint/@scrypted/nvr/public/#/iframe/${cameraId}`;
            }
            else {
                console.log(data);
            }
        })
    </script>
</head>

<body>
</body>

</html>

Just wanted to let everyone know, that I struggled with the same issues, integrating Grafana in Lovelace.

My root need was to have a nice graph, updated regularly and with many configuration option.

So after using ChatGPT, I came to the solution of using " Lovelace Mini Graph Card" in HACS.

It may not be as powerful or beautiful as Grafana, but for a Lovelace card, itā€™s pretty amazing!

Hereā€™s the link to this doc for this card: https://github.com/kalkih/mini-graph-card/blob/master/README.md#example-usage

Enjoy!

1 Like

ChatGPT is pretty awesome for coding! It even knows some node-red coding too. It can create and adapt flows and gives examples to paste in. Tho it does need a lot of correcting at times, it gives you a good base to start from!

Will take a look your link. It sounds great!

This solution work for me inside local net, outside local (behind the NAT on router) and any another start URL on HA app or browser.

Check it.

  1. Add vars in Addons-Grafana-Configuration tab:
env_vars:
  - name: GF_INGRESS_USER
    value: anonymous
  - name: GF_DEFAULT_INSTANCE_NAME
    value: Hassio
  - name: GF_AUTH_ANONYMOUS_ENABLED
    value: 'true'
  - name: GF_SECURITY_ALLOW_EMBEDDING
    value: 'true'
  - name: GF_SECURITY_COOKIE_SAMESITE
    value: none
  - name: GF_SECURITY_COOKIE_SECURE
    value: 'true'
  - name: GF_SECURITY_LOGIN_REMEMBER_DAYS
    value: '365'
  - name: GF_AUTH_BASIC_ENABLED
    value: 'true'
  1. No need change port for Grafana. Restart Grafana addon.
  2. Change URL on your HA cards:
  • delete address ā€œhttp://IPAddress:8123ā€ from Grafana panels urls.
  • new links should look like ā€œ/api/hassio_ingress/ā€¦ā€
  1. Clear cache browser or delete data in HA app.
  2. Login in HA and enter in Crafana web first. After that all panels starts loading.

Hope this help

1 Like

This really works for the admin user. In my case, it doesnā€™t work for a normal user (non-admin user). I researched the problem and will share my experience. I have Nginx before HA for SSL termination (because for this test project I use a server in hosting with other projects). So, the admin opens Grafana in an iframe but the user does not, he have error 401: Unauthorized. I found that the admin has a cookie ingress_session=23123sadsda23123-example_string , but my user doesnā€™t have this cookie. After manually adding the cookie, I found that my user now works and the 401: Unauthorized error has gone away. So, I will try to add this cookie to the Nginx config like this:

location ^~ /api/hassio_ingress/xxxxxxxxxxx/ {
    proxy_pass http://YOU_IP_HA_system:3000;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    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 Cookie "ingress_session=23123sadsda23123-example_string";
}

Where xxxxxxxxxxx (you can get in link for you dashboard before /d/)I think itā€™s a location for the Grafana container. Because I see it in Grafana docker nginx config

root@a0d7b954-grafana:/# cat /etc/nginx/servers/direct.conf 
server {
    listen 80 default_server;

    include /etc/nginx/includes/server_params.conf;
    include /etc/nginx/includes/proxy_params.conf;

    location / {
        return 302 $scheme://$http_host/api/hassio_ingress/xxxxxxxxxxx/;
    }
    location /api/hassio_ingress/xxxxxxxxxxx {
        rewrite  ^/api/hassio_ingress/xxxxxxxxxxx/(.*)  /$1 break;
        proxy_pass http://backend/;
    }
}

I donā€™t know about secure for this setup but itā€™s work.
P.S. First time I tried add my user admin privileges and Grafana work well, but I need that my user have only user privileges.
Maybe this information will be helpful to us for fixing this problem.

Tested this proxy_hide_header X-Frame-Options work too, and you can delete coockie)

1 Like

Ok, so there is a workaround (but not the solution). I was having the same issue (Unauthorised 401) on my ipad. Once I manualy opend Grafana from the left menu, it also started working in the included iframe.

Hope this helps to some of you at least a bit ā€¦

Best!