AxisCommunications / media-stream-library-js

JavaScript library to handle media streams on the command line (Node.js) and in the browser.
MIT License
302 stars 101 forks source link

Digest auth using media-stream-library unsuccessful #204

Closed kulwantsharma closed 3 years ago

kulwantsharma commented 5 years ago

Hi,

We were trying to stream live video of axis cameras through media-stream-library.js V6.0(https://github.com/AxisCommunications/media-stream-library-js/releases/tag/v6.0.2).

We tried your github example (See attached) to modify and perform basic/digest auth but couldn't succeed, we have also referred your source code index.ts and Html5VideoPipeline.ts (Seems to have digest implementation), to see if we can do digest auth in anyway, but couldn't succeed. We will be integrating the video in our application and we do not need the browser to prompt for the username and password. Instead, we want to use digest authentication as a standard way to authenticate to the camera.

Thanks, Kulwant Sharma

fabianeichfeldt commented 5 years ago

Hi, can you provide some more details about your test implementation? I' ve tested digest authentication successfully this way (in h264-player.js):

const pipeline = new pipelines.Html5VideoMetadataPipeline({
    ws: { uri: `ws://${host}:8854/` },
    rtsp: { uri: `rtsp://10.0.x.x:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif` },
    auth: { username: "admin", password: "secret" },
    mediaElement
  })
kulwantsharma commented 5 years ago

Hi,

After your suggestion, I tried in the following way but still I could not make it.

 var runPipeLine = new pipelines.Html5VideoMetadataPipeline({
        ws: { uri: 'ws://' + host + '/rtsp-over-websocket' },
        rtsp: { uri: 'rtsp://' + host + '/axis-media/media.amp?videocodec=' + encoding },
        auth: { username: 'root', password: 'root' },
        mediaElement
      });
      runPipeLine.ready.then(function () {
        return runPipeLine.rtsp.play();
      }).catch((err) => {
        console.log(err);
      });

Getting the below error.

Firefox can’t establish a connection to the server at ws://192.168.1.10/rtsp-over-websocket.

failed to load token 404

Error 404 Not Found

HTTP ERROR 404

Problem accessing /axis-cgi/rtspwssession.cgi. Reason:

    Not Found

error ​ bubbles: false ​ cancelBubble: false ​ cancelable: false ​ composed: false ​ currentTarget: null ​ defaultPrevented: false ​ eventPhase: 0 ​ explicitOriginalTarget: WebSocket { url: "ws://192.168.1.10/rtsp-over-websocket", readyState: 3, bufferedAmount: 0, … } ​ isTrusted: true ​ originalTarget: WebSocket { url: "ws://192.168.1.10/rtsp-over-websocket", readyState: 3, bufferedAmount: 0, … } ​ returnValue: true ​ srcElement: WebSocket { url: "ws://192.168.1.10/rtsp-over-websocket", readyState: 3, bufferedAmount: 0, … } ​ target: WebSocket { url: "ws://192.168.1.10/rtsp-over-websocket", readyState: 3, bufferedAmount: 0, … } ​ timeStamp: 1907 ​ type: "error" ​ <get isTrusted()>: function isTrusted() ​

: EventPrototype { composedPath: composedPath(), stopPropagation: stopPropagation(), stopImmediatePropagation: stopImmediatePropagation(), … } EntsecVideoConsole.js:135:17 Thanks, Kulwant Sharma
steabert commented 5 years ago

The auth component handles authentication to RTSP, but on Axis cameras you need to get a token via /axis-cgi/rtspwssession.cgi which seems to be missing. What is the firmware version of your camera?

kulwantsharma commented 5 years ago

I was using the below cameras.

  1. AXIS M1065-L Network Camera Firmware version: 8.20.1

  2. AXIS P12/M20 Network Camera Firmware version: 5.50.6

Thanks, Kulwant Sharma

kulwantsharma commented 5 years ago

Could you please help on this ?

steabert commented 5 years ago

Hi,

please don't spam the comments with "please help me" messages, these don't contribute to answering your question and they clutter the thread. I've hidden the comment now, so please stay on-topic. The help you get here is purely a best-effort from the developer community, and cannot be seen as a support line.

Now, on-topic:

I think firmware version 5.50.6 is too old, as far as I know there is no WebSocket RTP solution for that firmware (I guess it's 6.50+ that has this).

For your other camera, please check if /axis-cgi/rtspwssession.cgi is actually available (it should be).

There is also a camera example included in the code: https://github.com/AxisCommunications/media-stream-library-js/blob/master/examples/browser/camera/simple-player.js The example uses digest authentication to access the camera, instead of sending auth over RTSP.

sainadh6414 commented 5 years ago

There is also a camera example included in the code: https://github.com/AxisCommunications/media-stream-library-js/blob/master/examples/browser/camera/simple-player.js The example uses digest authentication to access the camera, instead of sending auth over RTSP.

I wasn't aware that simplePlayer.js is already authenticating over digest, thanks for clarifying the same, however in the provided example, window.fetch(`http://${ host }/axis-cgi/usergroup.cgi`, fetchOptions); triggers the browser to launch a popup and from there on the token exchange etc.. is taken care by the browser, it's not desirable for our application, we do not want to bother our users with this extra popup.

Is there any way that we can pass the username, password and authentication scheme through headers ? and bypass the window.fetch piece? It would be really great if you can throw some light here.

sainadh6414 commented 5 years ago

Hi, can you provide some more details about your test implementation? I' ve tested digest authentication successfully this way (in h264-player.js):

const pipeline = new pipelines.Html5VideoMetadataPipeline({
    ws: { uri: `ws://${host}:8854/` },
    rtsp: { uri: `rtsp://10.0.x.x:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif` },
    auth: { username: "admin", password: "secret" },
    mediaElement
  })

We are using the provided example and it doesn't have the rtsp object like the one you have specified. Can you help me on how we construct the rtsp url? also I understand from your code, that the authentication is happening over basic but not digest (it needs realm info etc...), please correct me if I am wrong.

Our code

play(host) {
        // Grab a reference to the video element
        const mediaElement = this.jq().find('video')[0];

        // Setup a new pipeline
        const pipeline = new pipelines.Html5CanvasPipeline({
          ws: { uri: `ws://${ host }:8854/` },
          rtsp: { uri: `rtsp://localhost:8555/test` },
          mediaElement
        });
        return pipeline.ready.then(() => {
          pipeline.rtsp.play();
        });
      };
kulwantsharma commented 5 years ago

Hi, Is there any other way to pass credentials through websocket in the below config?

ws: { uri: 'ws://XXX:XXX@'+host+'/rtsp-over-websocket', tokenUri:'http://'+host+'/axis-cgi/rtspwssession.cgi' },

Only this way it worked for us but in this request credentials are going as a plain text in the network as below which seems to be vulnerable. Request URL: ws://root:root@192.168.1.10/rtsp-over-websocket

In the below code, there seems to be no meaning of auth config. Credentials are not picking from auth config.

var runPipeLine = new Pipeline({
                 auth: { username: 'root', password: 'root' },
                ws: {
                    uri: "ws://" + host + "/rtsp-over-websocket",
                    tokenUri: `http://${ host }/axis-cgi/rtspwssession.cgi`
                },
                rtsp: { uri: "rtsp://" + host + "/axis-media/media.amp?videocodec=" + encoding },
                mediaElement: mediaElement
            });

Thanks, Kulwant Sharma

steabert commented 5 years ago

There is also a camera example included in the code: https://github.com/AxisCommunications/media-stream-library-js/blob/master/examples/browser/camera/simple-player.js The example uses digest authentication to access the camera, instead of sending auth over RTSP.

I wasn't aware that simplePlayer.js is already authenticating over digest, thanks for clarifying the same, however in the provided example, window.fetch(`http://${ host }/axis-cgi/usergroup.cgi`, fetchOptions); triggers the browser to launch a popup and from there on the token exchange etc.. is taken care by the browser, it's not desirable for our application, we do not want to bother our users with this extra popup.

Is there any way that we can pass the username, password and authentication scheme through headers ? and bypass the window.fetch piece? It would be really great if you can throw some light here.

If your application is a page in a browser, then no, there is no way to avoid the browser from popping up a window to handle the digest authentication. You can use either something like an Electron app and do the requests with NodeJS, or have a proxy between the camera and the application.

steabert commented 5 years ago

@kulwantsharma you are correct that in as far as an Axis camera is concerned, there is no point in having an auth module in the pipeline, since the WebSocket authentication happens with a token (that you get with the /axis-cgi/rtspwssession.cgi API), for which you need to be authenticated (with digest auth). The way our example works is to trigger digest auth, and only then play a video. The code will automatically try to get a token, and since you're authenticated, that will succeed.

What other people mentioned here is authentication over the RTSP protocol, but that's not relevant for Axis cameras if you stream RTSP over WebSocket.

So, in summary, there are two alternatives used in this library:

  1. get a token from an API behind digest authentication, use the token to open a WebSocket connection, and then use RTSP without any extra authentication. (This is used for Axis cameras, firmware >= 6.50)
  2. open WebSocket without authentication, but then perform basic or digest authentication over RTSP inside the WebSocket stream.

Hope this helps.

kulwantsharma commented 5 years ago

@steabert After getting token from API /axis-cgi/rtspwssession.cgi , I am not sure how to pass token to WebSocket . However I tried likeuri: "ws://" + host + "/rtsp-over-websocket?rtspwssession=${token}" but didn't get succeed.

or can you tell which api to call with the token along with some sample example.

steabert commented 4 years ago

The code that opens a WebSocket with a token is located in the openwebsocket.ts file, you can use this as an example. But if you use the library, you should be able to just use that function. Does it return a more specific error?

lekoaf commented 3 years ago

Closing due to inactivity. Feel free to open again if this was an error.