RocketChat / Rocket.Chat

The communications platform that puts data protection first.
https://rocket.chat/
Other
40.67k stars 10.66k forks source link

Rocket.Chat Iframe integration: browser navigation failure #8251

Open bakhtizin opened 7 years ago

bakhtizin commented 7 years ago

Description:

I am integrating Rocket.Chat to our existing web application, which has a large user base. After successfull Rocket.Chat Keycloak integration (SSO), it was decided to inject chat to the existing application using available iframe integration since it allows sending commands for authentication and simplifies Rocket.Chat events handling.

The actual problem is the fact that, iframe integration breaks site navigation, i.e. klicking browser's back button doesn't navigate to the previous page.

Server Setup Information:

Steps to Reproduce:

  1. Integrate Rocket.Chat as it is shown in Iframe integration
  2. Navigate in the web application (i.e. openning different menus, pages...)
  3. Click back button of the browser (Chrome, Mozilla)
  4. No matter how many times back button is clicked no navigation is taking place

Expected behavior:

Possibility to navigate back in browser

Actual behavior:

Clicking back button of the browser doesn't open previously open page

engelgabriel commented 7 years ago

@sampaiodiego can you please investigate this issue?

danieljhochman commented 7 years ago

FWIW, we don't have this issue in our iFrame integration. We are also running 0.58.2

bakhtizin commented 7 years ago

@sampaiodiego, @danieljhochman,

hoping that it can contribute to the understanding and possible solution, following is the short technical description of the actual Rocket.Chat integration:

<iframe id="rocketChat" src="#" data-src="${requestScope.rocketChatUrl}" frameborder="0"></iframe>
bakhtizin commented 7 years ago

@danieljhochman I didn't manage to overcome the navigation failure and curious how have you configured your system. Could you please help/shed some ligth on this issue. Many thanks.

hussainnayani commented 6 years ago

Any update on this issue? I am also having this issue.

anvlkv commented 6 years ago

this issue is pretty default. When your iframe's url changes it propagates to parent window history. If .go() back far enough you will get to rocket.chat login page and you're already authorized, so it redirects home. You can also go one more step back and that would be your intended back button action, but you have to know how many steps ago your iframe have loaded and there's no way forward. That's what I know. We are all doomed.

Is there some way to prevent meteor and flow router from changing window history?

I could for example postMessage to my window.parent from FlowRouter.triggers.enter([...]) and then in my app try to history.replaceState() but that doesn't make any difference.

anvlkv commented 6 years ago

Would that be a good idea to conditionally modify FlowRouter methods and call original methods from within FlowRouter.withReplaceState when ?layout=embedded?

engelgabriel commented 6 years ago

The documentation about Keycloak is going to be written. Please follow https://github.com/RocketChat/docs/issues/790 and fell free to help us :)

anvlkv commented 6 years ago

@engelgabriel could you explain please how Keycloack is going to be helpful in this situation?

BilalAlam173 commented 6 years ago

I encountered the same issue while integrating the rocketchat and following solution worked for me:

The Issue

1) when initiating the iframe either by writing explicitly in the html or appending it to the DOM through javascript , an new entry of rocketchat's url is pushed in the browser's history stack

2)If you are sending token from your parent application to the rocket chat in the iframe to login, Rocket chat will redirect if login is successful from the root_url to root_url/home creating a new entry in the browser history stack

3)Every time you click a anchor tag (enter a room etc) in the rocket chat (inside iframe) another entry is created and pushed in the browser history stack

Now when the app gets loaded the stack looks like this even if the iframe is hidden by default in the beginning

now if you press the back button the browser will go to rocketchat_url , the route will find out that a token is already present in the localstorage so it will redirect again to /home, therefore we will keep pressing the back button and it wont work

Solution

We need to avoid creating a new entry in the browser's stack for all the three points I mention above

1 & 2) Instead of giving url to the iframe in the html directly, we can add an empty iframe first, that will create a window object for that iframe , now we can access that window and use location.replace to set the url, this will replace the current entry in the browser history instead of creating a new one , this will work for the first point, for the second point we can directly give the '/home' url so that rocket chat doesnt have to redirect after login, if you are not login /home page will automatically fetch the token from your app if youre using postMessage to send the token.

$('body').append(`<iframe id="chat"></iframe>`);
$('iframe#chat')[0].contentWindow.location.replace(chat._baseUrl+'/home');

3)Adding an event listener on all the anchor tags in rocket chat that will replace the current entry in the history stack with the anchor tag's url so that when click is performed and browser tries to create new entry it finds the same entry already present on top of the stack and does not create a new entry

var anchors = document.getElementsByTagName('a');
        for (var i = 0; i < anchors.length; i++) {
            var anchor = anchors[i];
            anchor.addEventListener('click', function (event) {
                history.replaceState(null, null, anchor.href);
            }, false);
        }

^above code needs to be run inside the iframe window

Hope this will give you enough insight to solve the issue

barak-haviv commented 5 years ago
var anchors = document.getElementsByTagName('a');
        for (var i = 0; i < anchors.length; i++) {
            var anchor = anchors[i];
            anchor.addEventListener('click', function (event) {
                history.replaceState(null, null, anchor.href);
            }, false);
        }

^above code needs to be run inside the iframe window

is that really work to someone? It doesn't work for me

emmguyot-adscom commented 2 years ago

Hi, Any news about this bug ? @BilalAlam173 I understand your code and it works well as a standalone, but the problem is where could we put that code in Rocker.Chat ? The problem is that with React the DOM changes regularly so we should add the event each time the user interacts with Rocket.Chat. So it isn't quite simple.

As anyone tried to plug some code, such as the one above, in the React Router ?

emmguyot commented 2 years ago

Hi, Here is a patch that works for me.


index cd29aaeb8a..78fe22ab7c 100644
--- a/client/startup/routes.ts
+++ b/client/startup/routes.ts
@@ -184,4 +184,19 @@ FlowRouter.notFound = {

 Meteor.startup(() => {
        FlowRouter.initialize();
+       FlowRouter.triggers.enter([myTrigger]);
 });
+
+function myTrigger(context) {
+       window.history.replaceState({"path": context.path}, null, context.path);
+}
+
+window.pushStateOriginal = window.history.pushState.bind(window.history);
+window.history.pushState = function (state, x, path) {
+       if ((history.state !== undefined) && (history.state.path == path)) {
+               history.replaceState(state, x, path);
+       }
+       else {
+        window.pushStateOriginal(state, x, path);
+       }
+}```