"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

2 Likes

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!

hello i have same problem where should i set up this configuration in home assistant ?

Hi for now with HAOS I create this solution.
So we have problem when we user external address like bla.com
Because we have different routes to Grafana
In Grafana addons we have 2 nginx configs that work on 80 and 1337 port.

So when you go from left_panel you will always go like admin because you in allow ip address.
You got to 1337 port and we have header X-WEBAUTH-USER = admin it’s why we always will be admin from left panel because we use different routes from home assistant OS container.

For external requests(from user we will route to 80 port)

So if you use external link you will got to listen 80 port and get X-WEBAUTH-USER ‘’ -blank (we can saw it in /etc/nginx/includes/proxy_params that includes in this config).

So I fixed it to create custom location /api/home assistant OS_ingress/6lHRgBqnsurUG39JF5Wi1bo_bla_bla/ -< you can take it from share dashboard. And open in grafana addon setting port 3000(we use external port to Grafana container that route us to 80 listen nginx)

It’s real hard to debug all this stuff.
In my case I have users that not admin. And I want to give them access to it.If a miss smth you can try to write me)
proxy_hide_header X-Frame-Options; not work in my case I don’t know why maybe smth update

Update: in the next day )This setup broke.

I just thinked and now I understand all conception how it need to work.
So you need different end point to use it like anonim and admin.

I create second proxy host on NginxProxyManager grafana.blabla.com(we use it in lovelace to create visible ui) destination to ip:3000, where ip you ip homeassistant and 3000 port for worknig grafana.
And it’s work about 3 weeks everything ok.

I use traefik as a reverse proxy, and I have found a solution for me.

  1. Configure env_vars for Grafana
- name: GF_AUTH_ANONYMOUS_ENABLED
  value: "true"
- name: GF_AUTH_ANONYMOUS_ORG_NAME
  value: Main Org.
- name: GF_AUTH_ANONYMOUS_ORG_ROLE
  value: Viewer
- name: GF_SERVER_DOMAIN
  value: yourdomain.com
- name: GF_SECURITY_CSRF_TRUSTED_ORIGINS
  value: yourdomain.com
  1. In the grafana addmin network section use port 3000
  2. Login to Grafana and create a new admin user with strong password (not named admin, use some other name) and give it admin role
  3. Delete default admin user
  4. Add to grafana config parameter with admin name
grafana_ingress_user: your-new-grafana-admin
  1. configure traefik
http:
  routers:
    homeassistant:
      entryPoints:
        - websecure
      service: service-homeassistant
      rule: "Host(`yourdomain.com`)"
      tls: {}

    hassio_ingress:
      entryPoints:
        - websecure
      service: service-grafana
      rule: "Host(`yourdomain.com`) && PathPrefix(`/api/hassio_ingress/2c0f_7qPrQxj_MvkfjMDkKfdfjhgPPfjRRNMv3`)"
      tls: {}

  services:
    service-homeassistant:
      loadBalancer:
        servers:
          - url: "http://IP_OF_YOUR_HA_INSTANCE:8123"
    service-grafana:
      loadBalancer:
        servers:
          - url: "http://IP_OF_YOUR_HA_INSTANCE:3000"

PathPrefix you can find in grafana → share → Share embed

Now it finally works :heart: