IBM / video-streaming-web-player-api

This is the public Web Player API for controlling embedded players created at video.ibm.com.
Apache License 2.0
5 stars 6 forks source link

Player API doesn't work if SSO login dialog is presented #6

Closed pooshonbanerjee closed 3 years ago

pooshonbanerjee commented 3 years ago

In my organization the channel I work with is set up with SSO authentication. After every 24 hours the SSO token expires and the viewer is required to authenticate again.

In my application that makes use of Watson Media, the video is displayed in an IFRAME. When the user is required to authenticate again, this IFRAME displays a "Sign In" button as shown below:

image

After the sign in is complete, the video plays fine, but this is where the Player API breaks. The events contentAvailable and playing never triggers, the callMethod('play') and callMethod('seek') doesn't work.

When I check the dev console, I see an error thrown repeatedly:

Failed to execute ‘postMessage’ on ‘DOMWindow’: The target origin provided (‘https://extauth.services.video.ibm.com’) does not match the recipient window’s origin (‘https://localhost’).

image

My application client is written in Angular, and I declare that I have correctly imported the package:

import PlayerAPI from 'ibm-video-streaming-web-player-api';

The PlayerAPI object is initialized correctly:

this.viewer = PlayerAPI('playerWM');

The listeners are set up correctly:

    if(this.viewer && this.viewer.host){
      console.log("viewer available, adding listeners");
      console.log(this.viewer);
      this.viewer.addListener('playing', (event, data)=>{
        console.log("viewer playing -> ", event, data);
        if(data==false && this.autoComment){
          this.msgbox.nativeElement.focus();
          this.addTime();
        }
      });
      this.viewer.addListener('contentAvailable', (event, data)=>{
        console.log("content available for playing");
      });
  }

When I print the viewer object on dev console, this is what shows:

image

And I also declare that after the SSO sign in done and I refresh the page, all this would start working and keep working until the next time SSO sign in is required. Also the ‘postMessage’ on ‘DOMWindow’ error would not show up again.

So in my opinion the PlayerAPI breaks if the SSO sign in dialog is presented. Can you suggest a work around if a solution is not possible?

pooshonbanerjee commented 3 years ago

Since no one is looking at this, I am constantly trying to figure out a work around. I have found one that works, but not without its quirks. After the SSO dialog is gone, which would indicate that the authentication was successful, and the video is presented, if I reload the IFRAME, by simply doing a myIframe.src = myIframe.src, I can get the PlayerAPI initialize properly. All the events are properly bound, so they trigger correctly. The getProperty() methods works, and they return correct values, also the callMethod() functions start working correctly. The only place I am stuck is when to reload the IFRAME. To determine the right time and place I need to know if SSO dialog was presented or not, if it was, when was the authentication successful. I made a little progress in this direction too, but then got stuck again. I got the contentWindow object from the IFRAME. And I saw, when I printed the object on to the dev console, that once the auth is successful, this contentWindow object contains a property named ustream. But the irony is that I cannot read this ustream property of contentWindow because of cross-origin restrictions. If only the Apps and Integration section of the Watson Media settings presented a CORS setting, I could have easily set my application domain as CORS enabled and would have easily read the properties. Everything would be set. But now it seems there is no way but to wait for WM team to find a solution at their end. At one time I even thought if I call the getProperty('content') method, and look for an error, I could reload the IFRAME, but this method does not return anything if the Player API is not properly initialized. I am unable to even capture the postMessage error. So there is no way out. Waiting for help to come. @jungdaniel

jungdaniel commented 3 years ago

Hi @pooshonbanerjee, sorry for the late response. We are looking at this now, and I'll come back soon.

jungdaniel commented 3 years ago

It seems to me, that this use-case wasn't covered in the past, but I think we should support it, if feasible. What I found so far is that we rely on the value of document.referrer at some point, which doesn't seem to be a robust enough solution. I'll propose a fix and let you know if it can get into the to production player.

pooshonbanerjee commented 3 years ago

Sure thanks. I will be eagerly waiting since this is affecting a production application.

pooshonbanerjee commented 3 years ago

Hi @jungdaniel , is there any good new on this?

jungdaniel commented 3 years ago

Hi @pooshonbanerjee, the fix is under testing. Hopefully this weak it can go into the production player.

pooshonbanerjee commented 3 years ago

Thanks

pooshonbanerjee commented 3 years ago

Hi @jungdaniel , Any update on this?

jungdaniel commented 3 years ago

Hi @pooshonbanerjee, I know this is taking quite long. The fix went into production, but we introduced a small regression, so we rolled it back. The plan is to release it on next monday. (April 19)

pooshonbanerjee commented 3 years ago

Okay, looking forward to it. Thanks.

jungdaniel commented 3 years ago

Hi @pooshonbanerjee.

We introduced a new parameter, which should be added to the video's src you are embedding. Now it is called playerApiTargetUrl but we'll probably rename it to api-target-origin to better align with the params we have right now. The final name will go to the documentation. The param's name is api-target-origin and the its value should be the origin of the page from which the api is included.

For example, if I were to embed a video on github pages, with SSO configured, I would do it like this:

<iframe
  src="https://video.ibm.com/internal/embed/<id>?api-target-origin=https://jungdaniel.github.io"
  frameborder="0"
  allowfullscreen=""
  webkitallowfullscreen=""
></iframe>

It's worth mentioning that in some cases this param is not necessary. More specifically it should work out-of-the-box where location.ancestorOrigins is supported. (All major browsers except FF ...and IE) We removed this part. The param is necessary in every browser if someone wants to use the api with SSO.

Please test it and let us know your findings?

jungdaniel commented 3 years ago

Sorry, but we had to roll back this release again. We found a use-cause which wasn't covered.

pooshonbanerjee commented 3 years ago

Okay, I will wait. Thanks.

pooshonbanerjee commented 3 years ago

Hi @jungdaniel , any update on this?

jungdaniel commented 3 years ago

Hi @pooshonbanerjee,

Please try adding the api-target-origin to the embed url. Documentation: https://developers.video.ibm.com/player-api-usage#url-parameters

pooshonbanerjee commented 3 years ago

Hi @jungdaniel , I tried the solution mentioned in the documentation. Here are my observations:

Here is my IFRAME source code:

      var apiTargetOrigin = encodeURIComponent(this.rootURL); // this.rootURL contains the URL of the currently running app, which is localhost.
      this.iframeHTML = '<iframe id="playerWM" src="https://video.ibm.com/embed/recorded/'+asset_id+'?html5ui&showtitle=false&api-target-origin='+apiTargetOrigin+'" webkitallowfullscreen allowfullscreen frameborder="0" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-top-navigation-by-user-activation" width="100%" height="100%" style="position:absolute; top:0; left: 0">Loading...</iframe>';

Here is the IFRAME code from the dev console (inspect element)

<iframe id="playerWM" src="https://video.ibm.com/embed/recorded/127341066?html5ui&amp;showtitle=false&amp;api-target-origin=%2F%2Flocalhost%2F" webkitallowfullscreen="" allowfullscreen="" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-top-navigation-by-user-activation" style="position:absolute; top:0; left: 0" width="100%" height="100%" frameborder="0">Loading...</iframe>
  1. I got the following error when the SSO dialog was presented on the IFRAME. Please note this error shows up before the user has provided the log on credentials.

Failed to execute ‘postMessage’ on ‘DOMWindow’: The target origin provided (‘https://video.ibm.com’) does not match the recipient window’s origin (‘https://extauth.services.video.ibm.com’).

  1. After logging in, the error stays the same, just the error parameters changes:

Failed to execute ‘postMessage’ on ‘DOMWindow’: The target origin provided (‘https://extauth.services.video.ibm.com’) does not match the recipient window’s origin (‘https://localhost’).

The aforementioned error shows up 9 times, with the same parameters, and then stops occurring.

  1. The PlayerAPI doesn't initialize as earlier. The callMethod() method doesn't work, play/pause/seek also doesn't work.

If I am doing anything wrong, please let know.

P.S: I am using the latest version 1.3.0.

Regards

Pooshon

annatomka commented 3 years ago

Hi,

We checked the issue you mentioned above. The src is not looking right: https://video.ibm.com/embed/recorded/127341066?html5ui&amp;showtitle=false&amp;api-target-origin=%2F%2Flocalhost%2F

It looks like the query params are not separated correctly (there are &amp; characters instead of & characters), so the player could not receive the api-target-origin parameter.

Here is a simple example of adding an iframe dynamically. It was working for me with your video id (I used http-server to serve the static files on localhost).

<!DOCTYPE html>
<html>
<head>
    <title>Player API Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
<main class="container">
    <div class="player" id="playerContainer">
        <div class="controls">
            <button disabled class="play-pause">play</button>
        </div>
    </div>
</main>

<script src="./node_modules/ibm-video-streaming-web-player-api/dist/index.umd.min.js"></script>
<script>
    var apiTargetOrigin = encodeURIComponent(window.location.origin);
    var assetId = '127341066';

    var iframeELement = document.createElement('iframe');
    iframeELement.id = 'player';
    iframeELement.setAttribute('allowfullscreen', '');
    iframeELement.setAttribute('webkitallowfullscreen', '');
    iframeELement.setAttribute('frameborder', '0');
    iframeELement.width = '640';
    iframeELement.height = '480';
    iframeELement.src = 'https://video.ibm.com/embed/recorded/' + assetId + '?html5ui&showtitle=false&api-target-origin=' + apiTargetOrigin;

    document.getElementById('playerContainer').appendChild(iframeELement);

    var playing = false;
    var playPauseButton = document.querySelector('.play-pause');

    var api = PlayerAPI('player');

    api.addListener('playing', function (event, value) {
        playing = value;
        playPauseButton.innerText = playing ? 'Pause' : 'Play';
    });

    api.addListener('contentAvailable', function () {
        playPauseButton.disabled = false;
    });

    playPauseButton.addEventListener('click', () => {
        api.callMethod(playing ? 'pause' : 'play');
    });
</script>
</body>
</html>
pooshonbanerjee commented 3 years ago

Hi @annatomka ,

Thanks for your response. I re-checked my code and here are my observations:

  1. I am also setting the source how you have set in your code, using & (ampersand). The portion where you see &amp; in the SRC is from developer console -> inspect element. There all URLs are shown as encoded when copied from the console, that doesn't mean I am passing the parameters incorrectly.
  2. To double check if my parameter passing is correct, I passed several other parameters to check if they take effect, and they did. Here is what I passed:

this.iframeHTML = '<iframe id="playerWM" src="https://video.ibm.com/embed/recorded/'+asset_id+'?api-target-origin='+apiTargetOrigin+'&html5ui&showtitle=true&allowfullscreen=false&volume=50" webkitallowfullscreen allowfullscreen frameborder="0" sandbox="allow-forms allow-popups allow-same-origin allow-scripts allow-top-navigation-by-user-activation" width="100%" height="100%" style="position:absolute; top:0; left: 0">Loading...</iframe>';

I also switched the place for api-target-origin, it is the first one now, so there is no preceding &amp. The other parameters tell the player to show the title, disable the fullscreen button and keep the volume at 50%. All these parameters are taking effect. Please have a look:

image
  1. Here is the inspect element source:
image

I did not copy it and instead took a capture because copying it encodes the URI and you will see &amp; instead of &.

  1. I also want to mention that my code is in Angular 7 / Typescript and not JavaScript and hence it would not be possible for me to use the code exactly like you have shown. Typescript does not allow directly setting IFRAME source due to security reasons and it needs to be sanitized before setting. Given below is my HTML code and the pipe definition I am using.

HTML:

<div fxFlex="1 0 100%" *ngIf="currentAsset.assetType==='video'" [innerHTML]="iframeHTML | safe: 'html'" style="padding-bottom:56.25%; position:relative; display:block; width: 100%; border:1px none red;" (keyup.escape)="closeAsset()">Loading ...</div>

Pipe:

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml, SafeStyle, SafeScript, SafeUrl, SafeResourceUrl } from '@angular/platform-browser';

@Pipe({
  name: 'safe'
})
export class SafePipe implements PipeTransform {

  constructor(protected sanitizer: DomSanitizer) {}

  public transform(value: any, type: string): SafeHtml | SafeStyle | SafeScript | SafeUrl | SafeResourceUrl {
      switch (type) {
            case 'html': return this.sanitizer.bypassSecurityTrustHtml(value);
            case 'style': return this.sanitizer.bypassSecurityTrustStyle(value);
            case 'script': return this.sanitizer.bypassSecurityTrustScript(value);
            case 'url': return this.sanitizer.bypassSecurityTrustUrl(value);
            case 'resourceUrl': return this.sanitizer.bypassSecurityTrustResourceUrl(value);
            default: throw new Error(`Invalid safe type specified: ${type}`);
        }
  }
}

I'd again like to say that I believe the player is receiving every parameter correctly otherwise the title, fullscreen, and the volume control also would not have taken effect.

Kindly consider my observations.

Please also have a look at the following:

image

It says 301: permanently moved, then the next one ...

image

... says 302: Found. The URL changes and internal is inserted before embed. I don't know how this affects, just sharing what I can, to help.

And the following are the errors:

image

To me it seems the target origin is getting overwritten by https://extauth.services.video.ibm.com after being set to localhost. I am no expert, just speaking my mind.

Finally I have a question. When you said it is working for you, did you go through the SSO login too? I am asking because when I authenticate once, everything works for me too, for the next one hour till the SSO auth token stays valid.

Requesting you again to please consider my observations.

Regards

Pooshon

jungdaniel commented 3 years ago

Hi @pooshonbanerjee,

To be honest I don't know much about how sanitization work in Angular. I could be wrong but I wouldn't expect this issue to be Angular specific.

Just an idea: On your screenshot it seems you are passing api-target-origin=//localhost/. Have you tried passing the protocol as well? For example.: api-target-origin=http://localhost. Also is your local server running on port 80? If not, I would try to add that too. For example.: api-target-origin=http://localhost:4200. Let us know if any of this helped.

pooshonbanerjee commented 3 years ago

Hi @jungdaniel , Thanks a lot for constantly working on this with me.

Passing the protocol along with the URL finally worked. We can now close this ticket.

A big thanks to you and your entire engineering team.

Regards Pooshon

jungdaniel commented 3 years ago

Hi @pooshonbanerjee,

Great, thank you for your feedback! Let us know if you have suggestions on improving the docs on: https://developers.video.ibm.com/player-api-usage