HTTP Stream - Intermittent Auth Prompts From Browser
In ProgressHowdy,
I am trying to set up a livestream following this guide except I'm using the /media/ endpoint instead of HLS, and I'm using video.js for the player.
The goal is to have 100% uptime because it will be displayed on a kiosk screen with no user input. This is mostly working except occasionally I get prompted by Chrome/Firefox for a username/password in a pop-up:
If I cancel out of the prompt, the video reloads or continues without issue. However as mentioned, I need the video to play continuously with no user input. Last time I tested I had the stream running for about 90minutes before I saw the pop-up.
My understanding is that prompt would be the result of a 401 response from the server. Any suggestions for how to prevent this from happening?
Thanks!
-
Search results (e.g. this one) tell me one solution would be to set the auth type in the request headers to something other than Basic or Digest. The only 2 requests I'm making to the server are:
- /api/getNonce
- the full /media/<cameraID>.mp4?auth=.... URL which I'm setting as the video element's source
I'm assuming getNonce would never return 401, but I tried the header thing anyway and got an error that the header violates CORS policy.
For the second, I don't see a way to send or modify headers/metadata like that, and even if I could I'm guessing I'd get the same CORS error...
-
Hello Andrey,
OS: Windows x64
VMS build #: 5.1.0.37133
Code example:
<!DOCTYPE html>
<html lang="en">
<video id="video_element" muted autoplay></video>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.js"></script>
<link href="https://unpkg.com/video.js@7.0.0/dist/video-js.css" rel="stylesheet">
<script src="https://unpkg.com/video.js@7.0.0/dist/video.min.js"></script>
<script>
window.VIDEOJS_NO_DYNAMIC_STYLE = true;
var updateSrc = () => {
$.ajax({
url: serverAddress + "/api/getNonce",
type: "GET",
success: function (response) {
var realm = response.reply.realm;
var nonce = response.reply.nonce;
var digest = md5(username + ":" + realm + ":" + password);
var partial_ha2 = md5("GET" + ":");
var simplified_ha2 = md5(digest + ":" + nonce + ":" + partial_ha2);
var authKey = btoa(username + ":" + nonce + ":" + simplified_ha2);
var cameraURL = serverAddress + '/media/' + cameraId + '.webm?auth=' + authKey;
console.log(cameraURL);
player.src(cameraURL);
player.load();
player.play();
}
});
};
var username = '';
var password = '';
var cameraId = '';
var serverAddress = "https://" + window.location.hostname + ":7001";
var player = videojs('video_element', {}, function onPlayerReady() {
updateSrc();
videojs.log('Your player is ready!');
this.on('error', () => {
videojs.log('an error occured, retrying');
updateSrc();
})
this.on('ended', () => {
videojs.log('playback ended, reloading');
updateSrc();
});
});
</script>
</html> -
Hello,
Most probably, the window appears because the session timeout expires.
There are two things I'd recommend doing.
1. Use the token based authorization scheme
here are examples, in python though
https://github.com/networkoptix/nx_open_integrations/tree/master/python/examples/authentication.
2. Use this approach
you make the player and modify the beforeRequest so that it adds the x-runtime-guid header with the vms token
this.#videojs.Vhs.xhr.beforeRequest = options => {
if (!options.headers) {
options.headers = {};
}
if (this.authorization) {
options.headers[this.xRuntimeGuid] = this.authorization;
}
};And of course, you should check the token expiration period, which can be found in the 'expiresInS' parameter in the API response.
-
Thanks for the reply Andrey!
I suspect you're right about the session timeout, however I'm having trouble getting the token based auth to work. I can generate the token fine but everything I've tried still gives me a 401 (Unauthorized) when trying to load the src.
Everything I've tried includes:
- switching to HLS endpoint, as videojs.Vhs.xhr didn't seem to run when the source was webm or mp4. Guess HLS is required in this case?
- "setCookie": true when authenticating - am I right in thinking the x-runtime-guid header only works if this is the case?
- several variations of header syntax for the token:
options.headers['x-runtime-guid'] = token;
options.headers[this.xRuntimeGuid] = token; // (this.xRuntimeGuid was undefined)
options.headers['Authorization'] = 'Bearer ' + token;
options.headers['Authorization'] = token;
options.headers['Cookie'] = 'x-runtime-guid=' + token;
options.headers['Cookie'] = token;again, 401 in each case. Below is my current code, any pointers would be greatly appreciated:
<!DOCTYPE html>
<html lang="en">
<div id="player-wrapper" class="wrapper player h-0">
<video id="my-player" muted autoplay controls></video>
</div>
<link href="https://vjs.zencdn.net/8.5.2/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.5.2/video.min.js"></script>
<script>
window.VIDEOJS_NO_DYNAMIC_STYLE = true;
var username = '';
var password = '';
var cameraId = '';
var serverAddress = '';
var cameraURL = serverAddress + '/hls/' + cameraId + '.m3u8';
var settings = {
"url": serverAddress + "/rest/v2/login/sessions",
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/json"
},
"data": JSON.stringify({
"username": username,
"password": password,
"setCookie": true
}),
};
var initPlayer = () => {
$.ajax(settings).done(
function (response) {
var token = response.token;
var expiry = response.expiresInS;
var globalXhrRequestHook = (options) => {
debugger; // <-- wasn't hitting this breakpoint using /media/ endpoint, only hls
options.beforeSend = (xhr) => {
debugger;
options.headers = options.headers || {};
options.headers['x-runtime-guid'] = token;
};
return options;
};
videojs.Vhs.xhr.onRequest(globalXhrRequestHook);
var player = videojs('my-player', {});
player.addClass('d-flex');
player.addClass('hw-100');
player.src(cameraURL);
player.load();
player.play();
}
);
};
initPlayer();
</script>
</html>
Please sign in to leave a comment.
Comments
5 comments