ui3
ui3 copied to clipboard
Uncaught TypeError with Basic Auth: clipTimeline.getZoomScaler() undefined
When using UI3 with Basic Authentication (instead of secure session tokens/form login), when returning to a browser tab after an extended period (several hours) the following error occurs intermittently. This is also usually accompanied by the UI hanging at "Checking Session Status".
For context, I use basic auth in order to properly integrate BI with a forward auth proxy. However, the issue occurs even when I directly connect to BI, bypassing the proxy.
Uncaught TypeError: Cannot read properties of undefined (reading 'toFixed')
at https://[hostname]/ui3/ui3.js?v=277-5.9.9.3 [1452:132]
Environment
- Blue Iris Version: 5.9.9.3
- UI3 Version: 277
- Authentication Method: Basic Auth
- Browser: Chrome 130.0.0.0 (Mobile), as well as Firefox desktop.
- OS: Android 14 as well as Windows 11
Steps to Reproduce
- Configure UI3 to use Basic Authentication
- Load UI3 interface
- Error occurs intermittently when accessing timeline features
Other Observations
- Issue only occurs with Basic Auth, not with secure session tokens/form login.
- Error is intermittent, suggesting possible race condition in initialization.
- Full browser refresh (CTRL + F5) temporarily resolves the issue, sometimes it takes a few refreshes.
Code Location
The error occurs in the timeline zoom scale settings comment function:
{
key: "ui3_timeline_currentZoomScaleComment",
value: "",
inputType: "comment",
comment: function () {
return 'Current Zoom Scale: <span id="timelineCurrentZoomScaleComment">' +
clipTimeline.getZoomScaler().toFixed(1) + '</span>';
},
category: "Timeline"
}
That is bizarre because those comment functions are only called by the UI Settings panel, while the UI Settings panel is open. That should never be open while UI3 is loading or while directly accessing the timeline features.
I would suspect a badly misbehaving browser addon but you say it happens in Chrome on Android which doesn't even support browser extensions.
Do you have a ui3-local-overrides.js file or any other kind of script being injected by a proxy server?
You can replace that settings comment function with to get a full stack trace when the error occurs, and that should hopefully expose whatever is calling the comment function prematurely.
comment: function ()
{
var zoomScale = clipTimeline && clipTimeline.getZoomScaler() && clipTimeline.getZoomScaler().toFixed(1);
if (!zoomScale)
{
zoomScale = "unavailable";
try
{
throw new Error("clipTimeline.getZoomScaler().toFixed(1) would have thrown error");
}
catch (ex)
{
console.error(ex);
alert(ex.stack);
}
}
return 'Current Zoom Scale: <span id="timelineCurrentZoomScaleComment">' + zoomScale + '</span>';
}
The original error I posted was a red herring - that was happening after I tapped "open ui settings" - which explains why it was called.
I actually just got it to trigger on phone again. Steps to reproduce:
- Pull up chrome UI3 on Android.
- turn off screen or go do something else.
- Come back in 1-3 minutes.
- Errors abound.
I hooked my phone up to a USB debugger and pulled the following console log errors:
- Initial session expiration triggers a 403 Forbidden response
- UI attempts session recovery
- Timeline data loader makes requests that fail with {"result":"fail"}
- Each failure triggers new recovery attempts
- Process repeats indefinitely
Ok, interesting. So UI3 is failing to recover from the expired session when you have BI's secure session keys and login page disabled. That still works for me. Although it isn't as graceful as when secure session keys and login page are enabled, it does manage to recover a few seconds after the screen fills with error toasts. So if that isn't working for you, it must be something to do with your proxy server.
I wonder why the timeline requests are failing. Unfortunately Blue Iris is really bad about providing useful error messages, so I have to assume it is related to the session expiration, but I don't know why Blue Iris would include the session string in the response if the session was expired. So the timeline error might be something else.
I do see the issue where a timeline API request with result: "fail" would yield an instantaneous retry. The Update() method was designed to be called very rapidly and and make its own determination if an API request is required (if (!RequiresUpdatedView())). But it isn't throttling requests at all as a consequence of failures, so it will just rapidly retry indefinitely which is why your console logging is so spammy. I'll tweak that for the next UI3 release so it throttles a bit.
But the underlying issue is probably the session not being restored properly when it expires. I have no idea at all where that is breaking down, other than to believe it has to be related to your proxy server. I access Blue Iris through a regular reverse proxy server myself (just adding TLS), and it doesn't cause an issue with authentication.
If your proxy server is providing its own layer of user authentication, then I suggest you configure Blue Iris's web server to not require authentication, or perhaps just automatically grant access to the IP address that is your proxy server (e.g. put ^127.0.0.1 in the "Limit access by IP address" box to automatically give 127.0.0.1 admin privilege), and NOT use X-Forwarded-For / X-Real-IP headers,
Thanks for the input. Let me up the logging at various points and also see if I can snag an example of this happening sans proxy. I believe I've seen the same behavior when bypassing the proxy, but I need to prove that and get logs.
I can't turn off auth completely because I do have a few users with specific camera groups assigned.
So, I see 403 from both the proxied and non-proxied flows. As you suspected, the non-proxied version recovers gracefully after a few toast messages while the proxied version hangs.
I took a look at the headers and I noticed that the non-proxied version sends a clean Cookie: session= which I suspect is expected. The proxied version sends something very different with cookie: _forward_auth=redacted; authentik_proxy_opsdfRvIv=redacted; session=a-real-session-id
I expect to see Authentik cookies, but one very clear difference is the inclusion of an actual session cookie.
In the non-proxy case, when the stream ends with a session issue:
StreamEnded(..., isSessionLoss=true)
-> SessionManager.ReestablishLostSession()
ReestablishLostSession does a fresh login cycle:
args = { cmd: "login" } // Note: NO session included
But in the Initialize case (which the proxied version is hitting):
var oldSession = self.GetAPISession();
if (oldSession) {
ExecJSON({ cmd: "login", session: oldSession }, ...) // Includes stale session
}
The key difference is that ReestablishLostSession does a clean login without a session, while Initialize tries to validate an existing session first.
The proxy case appears to be:
- Stream fails
- Something is preventing isSessionLoss from being true in the StreamEnded call
- This causes it to go through initialization instead of session recovery
- Initialize sees an existing session and tries to use it
- This fails because the session is invalid, but now we're in the wrong code path
Does this shine light on the situation a bit?
Ok, unfortunately some things are still confusing me.
StreamEnded is called with isSessionLoss=true if and only if the request for an H.264 stream gets an HTTP 403 response from the server. It sounds like that is not happening for some reason in your proxy case?
The only way the SessionManager's Initialize method gets called is if the page has just loaded.
The intended code path is:
- Something clues UI3 into the fact that your session was expired. Could have been the H.264 stream getting an HTTP 403 response, or it could have been a json request getting a fail response (status, camlist, etc are called periodically).
- The
ReestablishLostSessionfunction gets called and your UI3 does not have a username and password saved because you didn't use Blue Iris's login page and tell it to save credentials. Soif (settings.bi_username && settings.bi_password)evaluates to false. And you don't have anonymous authentication enabled, soelse if (response.data["auth-exempt"])also evaluates false, so the code shows a warning that UI3 will reload in 3 seconds -- that warning may go unnoticed among other error toast spam. - Finally UI3 reloads the page which is intended to one way or another get you a new session (if secure session keys was enabled, BI would redirect the ui3.htm request to login.htm; otherwise BI would request HTTP authentication and the browser would handle it using the credentials you entered originally).
- During the page reload, it is assumed that your forward proxy is performing authentication via HTTP digest auth or basic auth or whatever it is that Blue Iris asked for, yielding a new session, which UI3 learns from the server via the line
var local_bi_session = "%%SESSION%%";inui3.htm. sessionManager.Initializegets called during UI3 startup, which sees there is an existing session (var oldSession). Since Blue Iris will not even serve the ui3.htm page if you don't have a valid session, this pretty much never fails to retrieve a session string.- UI3 proceeds to load normally.
Obviously something is breaking down along the way.
I still haven't figured out what could cause the timeline API requests to return "result":"fail" with a session string defined, and no other error message. I have to assume that for some reason your session got stuck in a bad state where it is not fully logged in and usable, but was usable enough to get the page to load.
I don't really know what causes the session cookie to get cleared in the non-proxy case, or what causes it to not get cleared in the proxy case.
Here's a thought. Is your proxy server doing any caching (specifically of the ui3.htm page)? If so that could be causing all of this havoc. I sure hope that answer is that easy.
I've confirmed via headers that no caching is happening.
I've now gotten this behavior to repeat without the proxy - that is when I'm directly talking to http://blueiris.lan:8053. I suspect that this is because I got BI to return Invalid session even without the proxy - not sure entirely how.
First thing I notice, the get to /video/site/2.0? returns a Set-Cookie with secure -- and Chrome blocks this because it's not HTTPS. This explains why my proxied path (HTTPS) is setting a session cookie successfully, but the non-proxied flow doesn't.
I'm still not sure if that matters at all. As of right now both proxied and non-proxied make it to self.ReloadAfterWarning(); and hang.
I'm still looking into this, but just wanted to share what I learned about the session cookie.
Edit: Seems to hang on json?__login:
Request:
POST /json?_login HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Authorization: Basic Yabc1234567no=
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 60
Content-Type: text/plain
Cookie: session=
Host: host.domain.com:8053
Origin: http://host.domain.com:8053
Pragma: no-cache
Referer: http://host.domain.com:8053/ui3.htm
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36
{
"result": "fail",
"session": "6c1066e010abc123454c8e71916ead",
"data": {
"reason": "Invalid session"
}
}
I assume I'm able to replicate this without the proxy because of the response reason I'm getting. However, even with the proxy I was getting "missing response" - so perhapos I've just triggered a new weird edge case.
Added a PR below to demonstrate a change that I think might be onto a possible solution. Let me know your thoughts as I'm admittedly stumbling through this a bit.
Blue Iris might be setting cookies with the secure flag if Blue Iris Settings > Web server has checked the checkbox Or Stunnel/other HTTPS is running on port. I leave that unchecked on my own systems where I do use a reverse proxy server to add https support. That is my best guess for why you have Cookie: session= with an empty value in the outgoing request to /json that you shared above. The browser was not able to read the session cookie when the page was not served via https. Fortunately the /json endpoint is supposed to ignore the session cookie and only consider the session string that is passed in via the JSON payload (but Blue Iris is closed source so we don't really know). Other supporting files that UI3 loads such as the .js and .css file(s) and svg graphics, those do rely on a session cookie typically so if that cookie fails to load it isn't going to be good. That said, UI3 does know how to work when cookies are disabled, but only when they are disabled by the web browser settings (then UI3 will always include the session as a URL parameter in requests that need it).
It is interesting that the session status check (in SessionManager's Initialize function) is getting an "Invalid session" message. I didn't know that could happen because that code only runs during UI3's loading phase which means ui3.htm just loaded from the web server. Blue Iris won't serve ui3.htm unless your session is valid. And Blue Iris writes your valid session string directly into the ui3.htm response body. So when SessionManager's Initialize function getting told the session is an "Invalid session", that is what makes me think you have something sending a cached ui3.htm file to your browser. Consider fully rebooting the OS that is running your proxy server because we've had at least one user in recent months who was having trouble with a proxy server and he thought he'd turned off all its advanced features but it turned out it was still running them until it got completely restarted.
I looked at your PR changes and it is curious. It seems that the stock UI3 behavior would have been:
- to not even consider the case where
data.reasonis"Invalid session" - to mark the session status check (part of the UI loading sequence) as failed
- to reload the page after 3 seconds
I assume that failure repeats indefinitely in your case.
In your PR changes, you had it detect the "Invalid session" reason and call ReestablishLostSession, which begins a new login handshake (sending {"cmd":"login"} with no session to the /json endpoint), except you've added code that looks at the response and expects to find session data returned from this request, which is supposed to be impossible because the request is made without a session argument at all. Blue Iris is only supposed to deliver challenge data from this request.
When you say
Seems to hang on json?__login: I am not sure what you mean by "hang".
First, I unchecked Or Stunnel/other HTTPS is running on port and it appears to have resolved secure being included on cookies. I can now set cookies via HTTP.
Regarding what I mean by hang:
Looking at Initialize(), we see the flow:
- Gets oldSession = self.GetAPISession()
- Makes request: ExecJSON({ cmd: "login", session: oldSession }...)
In logs, I got back:
{
"result": "fail",
"session": "abc123",
"data": {
"reason": "Invalid session"
}
}
Looking at this code branch:
else if (response.result == "fail") {
if (response.data) {
if (response.data.reason == "missing response") {
// handle missing response...
}
else
errorInfo = JSON.stringify(response); // <- We hit this
}
else {
loadingHelper.SetErrorStatus("login", 'The current session is invalid or expired.', 3000);
return;
}
}
The code specifically handles "missing response" but our actual error "Invalid session" just falls through to errorInfo = JSON.stringify(response) and... nothing happens after that! The code keeps running but never actually handles the invalid session. I interpret this to be why the page "hangs" at Checking Session.
While I'm far from certain, I think I identified what appears to be a dead-end in the flow leading to the "hangs". Specifically, when the code encounters certain conditions (such as missing bi_username when using a proxy, or receiving an 'Invalid session' response from Blue Iris), it lacks a proper recovery path.
I'm still learning how Blue Iris's authentication responses are meant to work and my use of an authentication proxy adds complexity that wasn't part of the original design so I acknowledge I might be off base here.
My debugging from yesterday was mostly just seeing if I could confirm I was hitting that spot in the code and force it to move past it. In that experiment, I don't think I'm handling it in the right way due to my lack of knowledge on BI auth flows, but I did confirm that it at least handles that dead end and seemed to reload the page (in a half broken way I later saw).
Oh, I finally see the code path that makes it hang. Yikes. Obvious now in hindsight. I've pushed a change to fix that.
I'm running that code now. When I have invalid sessions, it catches it with Blue Iris says your session is invalid as expected, reloads -- but it's looping infinitely on that.
ExecJSON({ cmd: "login", session: oldSession }, function (response)
After the reload, inside SessionManager, we land on the above login. Reading that, It seems like we will get invalid session as long as we keep trying with the old session?
I tried removing session: oldSession to test that, but then it just loops on:
onFail(response, 'Blue Iris sent session data instead of an authentication challenge (probably indicates a Blue Iris bug): ' + JSON.stringify(args) + " -> " + JSON.stringify(response));
I think when we get into these edge case failures, we probably should login without old session, but it's currently putting me into branches UI3 didn't expect and I'm not sure what the best way to handle that is as a result.
Side note - I can trigger this by making the tab inactive for as little as ~1 minute. I've been ignoring why that is because it's helpful for troubleshooting. But, that seems odd that the sessions are being marked invalid so quickly.
Blue Iris logs out sessions that aren't used for 1 minute. UI3 tries to do a json status request every 45-50 seconds while the tab is inactive but maybe that isn't working or maybe Blue Iris is timing out the session earlier than 1 minute these days.
Anyway the question of the day here is why does Blue Iris let you load ui3.htm with an expired session? I'd like you to try refreshing your ui3.js file with the latest from git (I haven't changed it since yesterday, but you have), and go to the "Reload Interface" section and swap it out for this:
///////////////////////////////////////////////////////////////
// Reload Interface ///////////////////////////////////////////
///////////////////////////////////////////////////////////////
function ReloadInterface()
{
isReloadingUi3 = true;
var url = RemoveUrlParams("session", "cacheBust", "startupSession");
if (location.hash)
url = url.substr(0, url.length - location.hash.length);
url += (url.indexOf("?") === -1 ? "?" : "&") + "cacheBust=" + Date.now() + location.hash;
location.href = url;
}
function AddStartupSessionToUrl()
{
var url = RemoveUrlParams("startupSession");
if (location.hash)
url = url.substr(0, url.length - location.hash.length);
url += (url.indexOf("?") === -1 ? "?" : "&") + "startupSession=" + local_bi_session + location.hash;
history.replaceState(history.state, "", url);
}
AddStartupSessionToUrl();
This changes two things:
- When UI3 tries to reload itself, it will do so with a new
cacheBust={current timestamp}URL parameter which should invalidate any unexpected cache (unless the cache REALLY misbehaves badly). - Upon startup, it displays the the session which was written into the
ui3.htmbody by adding it to the address bar as another URL parameter (using the HTML5 history API so as to not cause page navigation when adding the URL parameter).
If the above code resolves the issue entirely then we've identified a misbehaving cache as the root of your troubles.
If you still get stuck with the "Invalid session" failure, looping indefinitely, then you can determine if the cacheBust URL parameter is changing each time (I'd be shocked if it wasn't), and more importantly determine if the startupSession URL parameter is changing each time. If startupSession is remaining the same despite getting "Invalid session" failures, then you have likely encountered either a very badly misbehaving cache that serves up the same stale html page despite having unique URL parameters in the request, or you have encountered a bug in Blue Iris's session management.
Lastly, I do notice in Blue Iris's help file there are some new(ish) HTML Macros we could use for further troubleshooting, primarily %%AUTHSTATUS%% which would give us a clue of the session's status upon page load before any javascript even runs. Also %%AUTHORIZATION%% which could be used to pull the HTTP basic authentication credentials into javascript and potentially use them to create a new session for UI3 to use for API access. But that would be a last resort I think, only if we can't resolve the underlying issue.
My last reply above was a bit mangled; has been edited.
- Yes, I am still stuck in the loop.
- Yes, the cacheBust URL parameter is changing each time
- No, startupSession URL parameter is not changing each time
- Cookie: session= is not changing each request, but does change on some requests. I can't quite ascertain the pattern, it seems to change 1-2 times in the beginning of the loop before settling into the same. Not 100% on that, seeing some mixed behavior.
I'm having a hard time making sense of the sessions here. The json_login request has a different session in the request body vs. the session cookie. Also, when in the loop - the login response flaps between a failure and a success with each loop. That is a flap between Blue Iris says your session is invalid. Reloading momentarily... and Blue Iris sent session data instead of an authentication challenge (probably indicates a Blue Iris bug): {"cmd":"login"}.
Also, FWIW - Android Chrome browser does not continue making requests when the tab is inactive.
My path is Cloudflare -> Authentik -> BI. I have 1000% confirmed that CF isn't caching because Cache-control:no-cache, no-store, it's always been configured to respect query string, and now to be safe I've also explicitly disabled caching for all requests. I do not believe Authentik does any caching, but I am investigating. Finally, the startupSession does change fairly frequently as I'm refreshing/navigating, it just doesn't change during the loop. I feel like if caching was happening, it would be stickier (e.g. - if CF was caching, it's set to 4 hours).
curl 'https://host.com/ui3.htm?t=timeline&group=site_name&cacheBust=1731635053100' \
-H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
-H 'accept-language: en-US,en;q=0.9' \
-H 'cache-control: no-cache' \
-H 'cookie: authentik_proxy_opHmKvIv=DF2RYZ4OO42ILQ; session=266178015ed96fab7e494c2d179a2a8f' \
-H 'pragma: no-cache' \
-H 'priority: u=0, i' \
-H 'referer: https://host.com/ui3.htm?t=timeline&group=site_name&cacheBust=1731634829010&startupSession=110c73af5b77483e54085b660c135f9e' \
-H 'sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"' \
-H 'sec-ch-ua-mobile: ?1' \
-H 'sec-ch-ua-platform: "Android"' \
-H 'sec-fetch-dest: document' \
-H 'sec-fetch-mode: navigate' \
-H 'sec-fetch-site: same-origin' \
-H 'upgrade-insecure-requests: 1' \
-H 'user-agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36'
curl 'https://host.com/json?_login' \
-H 'accept: application/json, text/javascript, */*; q=0.01' \
-H 'accept-language: en-US,en;q=0.9' \
-H 'cache-control: no-cache' \
-H 'content-type: text/plain' \
-H 'cookie: authentik_proxy_opHmKvIv=DF2RAQYXMPO42ILQ; session=1c39501150e114ce55cd266e31c46597' \
-H 'origin: https://host.com' \
-H 'pragma: no-cache' \
-H 'priority: u=1, i' \
-H 'referer: https://host.com/ui3.htm?t=timeline&group=site_name&cacheBust=1731635053100&startupSession=4d3512157ae109824e49115742ab33b3' \
-H 'sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"' \
-H 'sec-ch-ua-mobile: ?1' \
-H 'sec-ch-ua-platform: "Android"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: same-origin' \
-H 'user-agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36' \
-H 'x-requested-with: XMLHttpRequest' \
--data-raw '{"cmd":"login","session":"4d3512157ae109824e49115742ab33b3"}'
curl 'https://host.com/json?_status' \
-H 'accept: application/json, text/javascript, */*; q=0.01' \
-H 'accept-language: en-US,en;q=0.9' \
-H 'cache-control: no-cache' \
-H 'content-type: text/plain' \
-H 'cookie: authentik_proxy_opHmKvIv=DF2REPO42ILQ; session=1c39501150e114ce55cd266e31c46597' \
-H 'origin: https://host.com' \
-H 'pragma: no-cache' \
-H 'priority: u=1, i' \
-H 'referer: https://host.com/ui3.htm?t=timeline&group=site_name&cacheBust=1731635053100&startupSession=4d3512157ae109824e49115742ab33b3' \
-H 'sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"' \
-H 'sec-ch-ua-mobile: ?1' \
-H 'sec-ch-ua-platform: "Android"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: same-origin' \
-H 'user-agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36' \
-H 'x-requested-with: XMLHttpRequest' \
--data-raw '{"cmd":"status","session":"4d3512157ae109824e49115742ab33b3"}'
When you get the message "Blue Iris sent session data instead of an authentication challenge (probably indicates a Blue Iris bug)", can you tell from network request logs exactly what the request body and response body ended up being? For that code path the request body is supposed to be exactly {"cmd":"login"} (assuming none of your own modifications were in the ui3.js file). If that is the case, then the fact you get this error message means Blue Iris did something unexpected with that request. What exactly Blue Iris did that was unexpected would depend on exactly what the response body text was.
For your information about how the json login command is supposed to work:
The json login command is supposed to have some very specific behaviors and the message above indicates that UI3 thinks Blue Iris did not adhere to one of those expected behaviors. Which means either you left some incorrect modification in your ui3.js code or Blue Iris is doing the wrong thing.
The JSON login ceremony is done with two very specific requests:
- First you call the
/jsonendpoint with nosessionfield, e.g. POST to/jsonwith request body{ "cmd": "login" }. Blue Iris is supposed to return a result that is something like this:{ "result": "fail", "data": { "reason": "missing response" }, "session": "........" }. The purpose of this first request is purely just to get a brand new unauthenticated session so it can be used in step 2: - Second, you call the
/jsonendpoint with asessionfield and aresponsefield, whereresponseis the base64-encoded md5 hash of the stringuser + ":" + session + ":" + pass. E.g.{"cmd":"login","session":"..........","response:".........."}. If the credentials you used were correct, Blue Iris is supposed to complete the login; your session is now authenticated and Blue Iris returns a response with"result": "success"and includes the session status. Otherwise Blue Iris is supposed to return a response with"result": "fail".
Those are the first two functions of the JSON login command, but there is a third: If you do the login command with a session field but no response field, Blue Iris is supposed to recognize this as a query to retrieve the status of the session, so if the session is valid, Blue Iris delivers a "success" result with the session status exactly like it does when you complete the login ceremony. Otherwise Blue Iris delivers a "fail" result with a data.reason of "Invalid session".
You've surely noticed that UI3 includes the command name in a little URL parameter when it POSTS to the /json endpoint (e.g. /json?_login). Let me just clear up any possible confusion here and tell you that URL parameter was purely my own invention to make it easier to identify requests in the browser's developer tools; Blue Iris doesn't care anything about it which is why you don't find it mentioned in Blue Iris's documentation at all.
Also, FWIW - Android Chrome browser does not continue making requests when the tab is inactive.
Yeah, on mobile devices, web pages in the background aren't allowed to run their scripts in the background at all as it would be considered a massive waste of battery. But on a desktop OS like Windows, script execution just gets throttled a bit. In theory, any setTimeout style of scheduling gets reduced to a precision of 1 second in order to limit how often repetetive scripts get run, but I haven't tested that in a long time so I don't know for sure anymore.
This is the request and response:
Blue Iris sent session data instead of an authentication challenge (probably indicates a Blue Iris bug): {"cmd":"login"} -> {"result":"success","session":"...","data":{"system name":"Site Name","admin":true,"changeprofile":true,"ptz":false,"audio":true,"clips":true,"clipcreate":true,"timelimits":false,"sessionlimit":0,"streamlimit":3600,"daylimit":0,"dayused":0,"dio":false,"version":"5.9.9.6","license":"..., Extended, 8/2/2025","support":"8/2/2025 [Extended]","user":"user","latitude":00.2527,"longitude":-00.7585,"tzone":"-300","streams":["VBR q=50%; gop=30; d2w *","CBR 1024kbps; gop=300; d2w *","CBR 2000kbps; gop=300; d2w *"],"sounds":["airhorn.wav","alarm-frenzy.wav","alarm.wav","alarming.wav","alien-message.wav","alien-tune.wav","are-you-kidding.wav","attention-required.wav","blocker.wav","decay.wav","demonstrative.wav","determined.wav","doorbell.wav","enough-with-the-talking.wav","gentle-alarm.wav","gesture.wav","good-morning.wav","goodbye.wav","hail.wav","hell-yeah.wav","high-pitch.wav","i-demand-attention.wav","i-saw-you.wav","job-done.wav","just-like-magic.wav","long-chime-sound.wav","may-i-have-your-attention.wav","munchausen.wav","news-bringer.wav","no-way.wav","not-kiddin.wav","oh-really.wav","on-serious-matters.wav","paranoid.wav","pizzicato.wav","police.wav","pop.wav","professionals.wav","quiet-knock.wav","robot-walking.wav","served.wav","sorted.wav","springy.wav","surprise-on-a-spring.wav","system-fault.wav","the-squeaky-wheel-gets-the-grease.wav","this-is-it.wav","warning.wav","what.wav","wiggle.wav","will-you.wav","you-wouldnt-believe.wav"],"www_sounds":["airhorn.mp3","alarm-frenzy.mp3","alarm.mp3","alarming.mp3","alien-message.mp3","alien-tune.mp3","are-you-kidding.mp3","attention-required.mp3","blocker.mp3","decay.mp3","demonstrative.mp3","determined.mp3","doorbell.mp3","enough-with-the-talking.mp3","gentle-alarm.mp3","gesture.mp3","good-morning.mp3","goodbye.mp3","hail.mp3","hell-yeah.mp3","high-pitch.mp3","i-demand-attention.mp3","i-saw-you.mp3","job-done.mp3","just-like-magic.mp3","long-chime-sound.mp3","may-i-have-your-attention.mp3","munchausen.mp3","news-bringer.mp3","no-way.mp3","not-kiddin.mp3","oh-really.mp3","on-serious-matters.mp3","paranoid.mp3","pizzicato.mp3","police.mp3","pop.mp3","professionals.mp3","quiet-knock.mp3","robot-walking.mp3","served.mp3","sorted.mp3","springy.mp3","surprise-on-a-spring.mp3","system-fault.mp3","the-squeaky-wheel-gets-the-grease.mp3","this-is-it.mp3","warning.mp3","what.mp3","wiggle.mp3","will-you.mp3","you-wouldnt-believe.mp3"],"profiles":["Inactive","Active","Night Time","No Alert","No Motion","Profile 5","Profile 6","Profile 7"],"schedules":["Default"],"news":false}}
You can't 100% trust what UI3 wrote in the error message to be exactly what was sent/received on the network, since there are layers of request processing code that could manipulate the request or response. You should check an actual network log or the Request and Response in the Network tab of the browser dev tools.
Anyway assuming those are actually what was sent and received on the network, it would indicate that Blue Iris's JSON API is bugged and is reading the session string from the wrong place. Because no session string is included in the request payload, the correct thing for Blue Iris to do is return an authentication challenge, NOT conjure a session out of some other place and return all that session data.
If you can confirm those are actually the payloads sent and received on the network, then you should open a ticket with Blue Iris support to let them know about the bug. Or I can do the ticket, if you can provide me with the appropriate network logs.
Fair enough, let me look a little closer at the response body directly and report back, it will likely be tomorrow. Thanks for your help!
Here's a more complete network log - same response - looks like the actual request does have a seesion? I'm seeing the same (probably indicates a Blue Iris bug): {"cmd":"login"} error which would indicate the logs aren't telling the correct story as you mentioned I think..
curl 'https://domain.com/json?_login'
-H 'accept: application/json, text/javascript, /; q=0.01'
-H 'accept-language: en-US,en;q=0.9'
-H 'cache-control: no-cache'
-H 'content-type: text/plain'
-H 'cookie: _forward_auth=lol; authentik_proxy_ololv=lol; session=3ee...123'
-H 'origin: https://domain.com'
-H 'pragma: no-cache'
-H 'priority: u=1, i'
-H 'referer: https://domain.com/ui3.htm?t=timeline&group=Site_Name&cacheBust=1731806041154&startupSession=3ee...123'
-H 'sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"'
-H 'sec-ch-ua-mobile: ?0'
-H 'sec-ch-ua-platform: "Windows"'
-H 'sec-fetch-dest: empty'
-H 'sec-fetch-mode: cors'
-H 'sec-fetch-site: same-origin'
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36'
-H 'x-requested-with: XMLHttpRequest'
--data-raw '{"cmd":"login","session":"3ee...123"}'
Response Headers:
access-control-allow-origin: * alt-svc: h3=":443"; ma=86400 cache-control: no-cache, no-store cf-cache-status: DYNAMIC cf-ray: 8e3bdc899f3e12d6-ATL content-encoding: zstd content-type: application/json date: Sun, 17 Nov 2024 01:14:00 GMT nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800} p3p: CP="CAO COR CURa ADMa DEVa OUR IND ONL COM DEM PRE" report-to: {"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=JV%2B7ItmCyGykjUa%2Fn8WoqLhD4XiaeWZdbKaaHtsIg6Qy5G0iKe4s%2Bu%2FPyQNpeyfiypvyS%2BpGj6uVrXxqX%2Feveic6BxRV9IX%2FvV6GMDcv37GqZvrJ3lMICfAPoTiMEQnPUPU%2FRA%3D%3D"}],"group":"cf-nel","max_age":604800} server: cloudflare server-timing: cfL4;desc="?proto=TCP&rtt=12592&sent=932&recv=526&lost=0&retrans=0&sent_bytes=944595&recv_bytes=16634&delivery_rate=6229857&cwnd=270&unsent_bytes=0&cid=074c2e69844bd404&ts=32129&x=0" :authority: domain.com :method: POST :path: /json?_login :scheme: https accept: application/json, text/javascript, /; q=0.01 accept-encoding: gzip, deflate, br, zstd accept-language: en-US,en;q=0.9 cache-control: no-cache content-length: 60 content-type: text/plain cookie: _forward_auth=lol; authentik_proxy_opHmKvIv=lol; session=3ee...123 origin: https://domain.com pragma: no-cache priority: u=1, i referer: https://domain.com/ui3.htm?t=timeline&group=Site_Name&cacheBust=1731806041154&startupSession=3ee...123 sec-ch-ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 x-requested-with: XMLHttpRequest
How sure are you that the request info you exported was from the first JSON request made by the LogInWithCredentials function? I just really don't see how it could have included the session string like that. To double-check, you might try modifying the args object for that to include a bit of debugging text to be able to tell it apart from other login commands with confidence.
e.g.:
var LogInWithCredentials = function (user, pass, onFail, isAutomatic)
{
var oldSession = self.GetAPISession();
var args = { cmd: "login", ui3debug: "LogInWithCredentials A" };
ExecJSON(args, function (response)
...
I don't think it is the first as this is almost always when I come back to an inactive tab and it spams these errors - it's really hard to find the first as the debugger will lose the body of older requests when so many come through.