# D2 Issue with authenticating proxy when token expires

**URL:** https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632
**Category:** Dashboard
**Tags:** dashboard-2
**Created:** [23 March 2026 10:12 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632 "2026-03-23T10:12:40Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [23 March 2026 10:12 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/1 "2026-03-23T10:12:40Z")

</div>

I have an issue running the flowfuse dashboard behind cloudflare zero-trust, using google to authenticate.  
When the token expires the dashboard is initially unaffected. It seems that the existing websocket connection continues ok even though the token has expired. That is ok. However, if I switch pages in the dashboard then bits of the dashboard do not appear and in the browser (edge) console I see stuff like

```auto
Access to script at 'https://me.cloudflareaccess.com/cdn-cgi/access/login/nodered.mydomain.uk?kid=...&redirect_url=%2Fresources%2F%40colinl%2Fnode-red-dashboard-2-ui-gauge-classic%2Fui-gauge-classic.umd.js' (redirected from 'https://nodered.mydomain.uk/resources/@colinl/node-red-dashboard-2-ui-gauge-classic/ui-gauge-classic.umd.js') from origin 'https://nodered.mydomain.uk'
 has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
GET https://me.cloudflareaccess.com/cdn-cgi/access/login/nodered.mydomain.uk?kid=...&redirect_url=%2Fresources%2F%40colinl%2Fnode-red-dashboard-2-ui-gauge-classic%2Fui-gauge-classic.umd.js net::ERR_FAILED 200 (OK)

TypeError: Failed to fetch dynamically imported module: https://nodered.mydomain.uk/resources/@colinl/node-red-dashboard-2-ui-gauge-classic/ui-gauge-classic.umd.js

```

Is there a solution to this?

Refreshing the page takes me to the login page, but it would be much better if this happened automatically.

---

<div class="post-metadata">

### Author: ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)
#### Post date: [23 March 2026 12:32 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/2 "2026-03-23T12:32:17Z")

</div>

Does this always happen when you switch pages, or only after token expiration?  
Do you want your page to go to login automatically and unsolicitedly once the token expires?

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [23 March 2026 12:58 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/3 "2026-03-23T12:58:57Z")

</div>

> [@Colin](#):
>
> When the token expires the dashboard is initially unaffected. It seems that the existing websocket connection continues ok even though the token has expired.

I've highlighted this as an issue many times. It isn't specifically a Node-RED issue but rather stems from the fact that websocket connections do not allow custom headers and therefore the server cannot verify if a session token is still valid.

There are ways to work around this, the easiest being to include any authentication/authorisation token in EVERY message that passes between client and server. This is why UIBUILDER has a set of hooks that allow processing like this to be added.

> [@Colin](#):
>
> `blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.`

I think this is basically saying that the browser will not allow access to that "foreign" resource origin.

You will need to adjust Node-RED's CORS settings to allow access.

I'm afraid I can never remember how to do this and always have to look up the correct CORS header.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [23 March 2026 13:43 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/4 "2026-03-23T13:43:54Z")

</div>

> [@omrid](#):
>
> Does this always happen when you switch pages, or only after token expiration?

Only if the token has expired. Also I suspect that it may only happen the first time that page is visited. Hopefully it is not fetching the contrib node every time the page is opened.

> [@omrid](#):
>
> Do you want your page to go to login automatically and unsolicitedly once the token expires?

I suppose that would be the ideal, though personally I don't have strong feelings about it. I suppose that if I can find where the resources are loaded then forcing a page reload when the error occurs would be ok.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [23 March 2026 13:46 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/5 "2026-03-23T13:46:18Z")

</div>

> [@TotallyInformation](#):
>
> You will need to adjust Node-RED's CORS settings to allow access.

I think I will investigate whether I can force a page reload when this happens, which will bring up the login page and all should be well.

---

<div class="post-metadata">

### Author: ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)
#### Post date: [23 March 2026 16:01 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/6 "2026-03-23T16:01:50Z")

</div>

I have done such an implementation at a live project, where the flow goes back automatically to login after a timeout (in case of token) or configurable time with no user activity. It also forces going through the login and blocks direct access to the page.  
Do you want me to share?

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [23 March 2026 16:59 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/7 "2026-03-23T16:59:46Z")

</div>

Thanks for the offer, but I think I have a solution.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [24 March 2026 14:48 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/8 "2026-03-24T14:48:26Z")

</div>

Unfortunately this was wishful thinking on my part. I have moved on from the particular case described above to the more serious one which arises if the auth session expires and then an attempt is made to open the websocket connection. Currently this fails and the code assumes that the server is unavailable and repeatedly retries the connection, which accomplishes nothing. The correct action is to force a page reload.

I have documented this in the issue below, but have not so far found a solution. If anyone who knows anything about such things could have a look at that and maybe offer some ideas that would be great.

> <https://github.com/FlowFuse/node-red-dashboard/issues/2074>
>
> \### Current Behavior
> 
> When using an authentication proxy such as cloudflare zero… trust and the authentication session times out whilst the page is inactive (for example the PC is suspended or when running on a mobile device the OS puts the browser to sleep) then a Connection Lost is shown when the page is activated and remains shown until 5 minutes have elapsed or the page is refreshed. 
> The same effect can be seen if the machine is left active, then after the session has expired disconnect the network connection between the browser and the server, wait (about 30 secs) for errors to appear in the developer console and then reconnect the network. The developer console will continue to show errors for 5 minutes before showing the login request.
> If the browser developer console is opened it can be seen that the software is repeatedly trying to open the websocket connection but this fails because it gets redirected to the login session. After 5 minutes this times out and the s/w reloads the page, which brings up the login screen.
> 
> \### Expected Behavior
> 
> The user should be taken to the login screen.
> 
> \### Steps To Reproduce
> 
> 1. Open a dashboard page in a browser on a PC
> 2. Suspend the PC
> 3. Wait for the authentication session to expire, or revoke the session
> 4. Resume the PC
> 
> OR
> 
> 1. Open a dashboard page in a browser on a PC
> 3. Wait for the authentication session to expire, or revoke the session
> 4. Disconnect the network between browser and node red 
> 5. Wait for transport errors in the developer console
> 6. Reconnect the network
> 
> \### Environment
> 
> \- Dashboard version: 1.30.2
> \- Node-RED version: 4.1.7
> \- Node.js version: 20.20.0
> \- npm version:
> \- Platform/OS: Ubuntu on Laptop
> \- Browser: Edge
> 
> 
> \### Have you provided an initial effort estimate for this issue?
> 
> I am not a FlowFuse team member

Preferably continue discussions on the issue page rather than here please.

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [24 March 2026 20:44 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/9 "2026-03-24T20:44:36Z")

</div>

> [@Colin](#):
>
> if the auth session expires and then an attempt is made to open the websocket connection. Currently this fails and the code assumes that the server is unavailable and repeatedly retries the connection

I don't know the internals of D2, but ...

When a [socket.io](http://socket.io) connection attempt starts (or a raw ws: connection for that matter), the initial connection is over http(s). This initial connection is the only place that custom http headers are supported (well, for [socket.io](http://socket.io), the fallback long-polling is http(s) so that supports it too but that is an aside). So what _should_ happen is that the [socket.io](http://socket.io) connection should have the same header processing as the main ExpressJS handler in terms of authentication and proxy related headers.

Sounds like this isn't happening. So I would suggest that this is probably a bug.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [24 March 2026 21:09 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/10 "2026-03-24T21:09:38Z")

</div>

> [@TotallyInformation](#):
>
> Sounds like this isn't happening.

What tells you that? I am not saying you are wrong, I am just trying to understand what is going on, we are outside my knowledge base here. The reason the connection attempt fails is that the initial https attempts to redirect to the login page (which is a cloudflare domain) and fails with a CORS error.

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [24 March 2026 22:01 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/11 "2026-03-24T22:01:55Z")

</div>

> [@Colin](#):
>
> What tells you that? I am not saying you are wrong, I am just trying to understand what is going on, we are outside my knowledge base here.

Hard won experience! Went through all of this (several times over the years) with UIBUILDER.

UIBUILDER has several places where you can add in custom [Socket.IO](http://Socket.IO) message processing so that you can hook in authentication and authorisation logic. These hooks for example which you can configure in the uibuilder section of your settings.js file:

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/e/2/e2258bed74afbbdb83c734005f020b1a1e8bde8b.png)

It has taken me a LOT of time and angst trying to understand the inner workings of websockets in general and [Socket.IO](http://Socket.IO) specifically.

> [@Colin](#):
>
> The reason the connection attempt fails is that the initial https attempts to redirect to the login page (which is a cloudflare domain) and fails with a CORS error.

Well, it is perfectly possible that I've misunderstood. I suppose that what you are really covering here is what the client does rather than the server? I do get a bit wrapped up with the server logic!

At the very least though, some additional logic is likely to be needed for when a [socket.io](http://socket.io) (re)connection attempt fails.

I don't know how this is managed in the D2 client libraries, I only know how I do it. And truthfully, my reconnection logic - though it works - isn't ideal. Indeed, I currently have a slightly related issue to yours in developing Markweb (single-node website creator in uibuilder). I want to reload the page after a client has disconnected and then reconnected - as happens a lot when your browser or its host goes into sleep mode and then back out. In the uibuilder client library, I expose custom events for this happening but the logic isn't quite right to be able to capture _only_ a reconnection and not the initial series of connections that happen normally (first connection is over http(s), then you may get several long polling connections and finally an "upgrade" connection - which you can see has a 101 response code in the network tab of your browser dev tools).

This issue is, in fact, the focus of tomorrow. 😃

Anyway, sorry for the side-track. Bottom line, you probably need a way to respond to [socket.io](http://socket.io) connection errors in the browser, not sure if they are exposed in D2.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [25 March 2026 10:38 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/12 "2026-03-25T10:38:49Z")

</div>

> [@TotallyInformation](#):
>
> suppose that what you are really covering here is what the client does rather than the server?

Yes.

> [@TotallyInformation](#):
>
> At the very least though, some additional logic is likely to be needed for when a [socket.io](http://socket.io) (re)connection attempt fails.

That is what I am trying to fix. I believe I now have the solution.  
In the client, before the socket tries to connect, perform a fetch of '\_setup' which is a small json file. This is what was giving the CORS error when cloudflare tried to redirect to the login page. The solution is to use `fetch('_setup', {redirect: 'manual'})` so that it will not actually do the transfer. Then I can check the response from that and check that the content-type header includes 'application/json'. If it doesn't then I will force a page reload so that the login screen will be shown.

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [25 March 2026 11:19 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/13 "2026-03-25T11:19:06Z")

</div>

Sounds good. Have you tested what happens when a CFZT session expires with an active websocket? Does the websocket remain active or does it die?

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [25 March 2026 11:42 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/14 "2026-03-25T11:42:16Z")

</div>

> [@TotallyInformation](#):
>
> what happens when a CFZT session expires with an active websocket?

It remains active. There have not been any complaints about that as far as I can remember, thought that does not seem to be the 'correct' behaviour to me.

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [25 March 2026 13:18 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/15 "2026-03-25T13:18:41Z")

</div>

> [@Colin](#):
>
> that does not seem to be the 'correct' behaviour to me

And it absolutely isn't. That was what I was wittering on about before. 😃 When an authentication session expires, there should only be enough communications to allow that fact to be known so that actions can be taken. No general communications, certainly no general data should be allowed at that point. There are a couple of ways to do that. But they aren't especially straight-forward.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [25 March 2026 14:15 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/16 "2026-03-25T14:15:40Z")

</div>

The code I described above can be used to detect it, so presumably running that on a polling basis once a minute (or whatever) and reloading the page when it happens would be good enough.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [25 March 2026 14:32 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/17 "2026-03-25T14:32:19Z")

</div>

I have opened [an issue](https://github.com/FlowFuse/node-red-dashboard/issues/2077).

How do you detect, in uibuilder, that the session has expired?

---

<div class="post-metadata">

### Author: ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)
#### Post date: [25 March 2026 15:50 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/18 "2026-03-25T15:50:59Z")

</div>

> [@Colin](#):
>
> How do you detect, in uibuilder, that the session has expired?

I don't. Because there are too many variables and too many different things people may want to do.

Also worth noting that such a check should NOT be done in the browser since that is not secure.

What I do is to make hooks and middleware options available on the server so that people can wire in checks that suite them.

On the client end, you can check for an expiry timestamp - noting that browsers don't generally have access to HTTP headers (though a web worker can access them) and so you need to do a process on the server and tell the client via a message about session info. The point about the client checks being that they are simply a convenience for users. Not a security feature.

To get per-message session security with websockets, you have to process every in- and out-bound message. Generally, this means keeping track of session info unless you have the info already in web headers. In that case, you have to check every message against the session expiry timestamp and put a hold on messages if the session has expired. Session expiry might be encoded into a JWT or might be in a different header, or might not be available at all! (In which case you would need a further server process to track times).

So now you know why - though I've tried many times over the decade or so of uibuilder's life - I don't make any attempt to try and process this.

I have middleware - which requires a JavaScript file containing some pre-defined functions, a bit of a pain to get right - or the newer hooks which can go into your settings.js. Both have functions for dealing with HTTP(S) and [Socket.IO](http://Socket.IO) web connections _and_ functions for intercepting incoming and outgoing messages so that you can do the per-message checks and blocks as needed.

Also worth noting that uibuilder uses separate message channels for control messages and user messages. This lets you block standard user messages but still allow control messages.

All very carefully thought through and architected to provide the flexibility required for all manner of requirements.

---

<div class="post-metadata">

### Author: ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)
#### Post date: [25 March 2026 15:55 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/19 "2026-03-25T15:55:32Z")

</div>

I think that all says that it can't really be done for a simple tool like the dashboard. At least not in a way that copes with the general case where we know nothing about the auth system in use.

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)
#### Post date: [24 April 2026 15:56 UTC](https://discourse.nodered.org/t/d2-issue-with-authenticating-proxy-when-token-expires/100632/20 "2026-04-24T15:56:13Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
