I have a web app, which is launched in a new pop-up window when a customer clicks a link on a Drupal web content page.
When the user reaches a certain page in the web application's workflow, I want to send a message to the (still open) Drupal page and pass it some information (like where in the workflow I'm up to).
When the Drupal website receives this 'message', it calls some javascript on its end to update the currently displayed page content.
So basically, a way to execute Javascript on one domain, from a Javascript function in another domain.
I have control over both the Drupal site and the web application so any javascript which needs to be deployed on either end isn't an issue. But note that the two sites are on different domains, so I need to implement something that will work cross-domain
I am not sure how to implement this, I have considered JSON-P so far.
Both your web apps are running at client-side, so you don't need to ask to server about how to contact with the window that you already opened.
So, to save time and effort, make a directly comunication between the windows at client-side.
To send a message to another html page, you need to first keep in a variable the window opened, as bellow:
// This code is inside the html file that open
// the drupal site.
var windowOpened = window.open("drupalUrl.html");
but, at drupalUrl.html you need to create a global function, so you can call like this:
windowOpened.globalFunction(param1, param2);
As I said, inside the drupalUrl.html you need to create a function:
function globalFunction(param1, param2){
//show your message
}
Related
I have made a website that enables users to create their own widgets and place them on their own websites. I want users of those websites to be able to log in to these widgets using Twitter, Facebook and Google. I have 99% of the process in place, but the remaining 1% is my stumbling block. The process I have implemented is as follows:
User creates a widget on my site (mysite.com)
User places some javascript on their own website (example.com) to embed the widget
The user of a widget clicks "Log in with Twitter"
A new window is opened (using window.open) which loads mysite.com/auth/twitter, redirecting them through the oAuth flow on twitter.com
All being well, the user gets redirected back to mysite.com/auth/twitter/callback in the new window and I store the user's details in the database on mysite.com.
At this point, the newly-created User ID should be passed back to the main window in which the widget is embedded. But, as far as I can tell, there is no way to do so because because window.opener in the new window is null (due to the redirects that have happened). Nor do I have a reference from the main window to the new window, also due to the redirects.
I have tried window.postMessage from the new window to the main window, directly accessing functions and variables in the new window from the main window, all to no avail. There is seemingly no way to reference any data or properties from either window.
Is there anything else I can try or do I have to implement the process without a new window somehow?
If it matters, mysite.com is built in Laravel and the social authentication process uses Socialite.
Any help is appreciated!
I have tried window.postMessage from the new window to the main window
postMessage works cross-domain, but you must in that case specify the second parameter (targetOrigin).
Taking into account your clarifications regarding how the widget is embedded, you are fairly limited in regards to communication options as you saw.
Since your child window now has no way to communicate with the parent, being without a window.opener reference and on a different domain, you could rely on the parent and have it track the child window progress like suggested here : https://stackoverflow.com/a/18804255/18706075
Also, not to be insistent on the iframe approach, but you could also add an invisible iframe opened to mysite.com. That way, the pop-up window that ends up on mysite.com has many options to make data available to the iframe on the same domain, and the guest iframe can easily communicate with the host window as I originally suggested :
Host listens to the message event
window.addEventListener("message", myMessageHandler);
Guest iframe notifies the host.
window.top.postMessage({authenticationResult: "whatever_value"}, 'http://example.com');
I couldn’t find a “proper” way to do this, so what I ended up doing was generating a unique code and passing that to the new window. This code gets stored in my database and updated with the ID of the user upon completion of the oAuth process. The main window uses setInterval to check for the existence of a user ID against the unique code every couple of seconds.
POPUPS AND IFRAME REDIRECTS
I would have a look at the oidc-client library, which used to do similar things. See the code in PopupWindow and the way that a named window object is used before the redirect and then used on the response. The OAuth state parameter was used to correlate the request and response:
// Before navigating
window["popupCallback_" + params.id] = this._callback.bind(this);
// Notify opener upon return
var name = "popupCallback_" + data.state;
var callback = window.opener[name];
callback(url, keepOpen);
The library also performed some interesting iframe navigation if you look at the IFrameWindow and IFrameNavigator classes. Using iframes can be permissioned with your own authorization server, but will not work with Google or Twitter due to clickjacking protections.
BROWSER RESTRICTIONS
One of the reasons why the above library is inactive is browser restrictions on content from third party sites, which impacts some of the browser OpenID Connect behaviour.
Your widgets will be treated in a hostile manner by browsers, who will apply the same restrictions as they do on third party ads that try to track users across sites.
In particular, if your widget gets data by calling mysite.com with an HTTP-only cookie, this will be considered a third party cookie and dropped by the browser. Access tokens can work, but they are not considered the browser current best security practice.
MAIN WINDOW REDIRECTS
If you can't get popups to work, consider redirecting on the main window, which will work best from a browser restrictions viewpoint. A main window redirect serves as a user gesture so that any login cookies from Google etc are not dropped. Your flow might work like this, though it requires a design based on access tokens:
Customer page loads at example.com
Widget loads in an iframe at mysite.com and renders content that prompts the user to click a button to authenticate
When the button is clicked, the widget saves the parent URL in session storage, then redirects the parent window to Google, with a mysite com redirect URI
Main window at mysite.com conpletes the login, saves an access token to session storage, then redirects back to the stored parent URL
Customer page loads again at example.com
Widget loads again in an iframe at mysite.com and this time can get an access token from session storage to use for data access
From a user experience viewpoint, the user signs in to the main app initially, then once more to the widget as a separate provider. The impact on the host app is a bit like refreshing the page, which it will already cope with.
FUTURE
The FedCM initiative is aiming to solve this type of cross domain browser identity problem in a future facing way. It will not be ready any time soon, but it is worth reading their docs to identify potential issues with your own solution.
I have to display a third party site, say site X, upon successful authentication from my existing APIs, as it is, on my web site. But due to certain reasons, I cannot afford to use an iframe, which is the most obvious solution in this case, as that will expose the site X's URL when inspected in browser. So, I landed up on another idea to create a web app whose front end is written in AngularJS and use existing back end APIs written in C#. The application will basically have two pages, login and main page. Upon successful authentication from API on login page, user is navigated to main page. Now main page, I have to show another site. So, instead of using iframe on main page having source as site X, I will display an iframe with source Url as one of my API endpoint, say baseURL/data. So, this API will basically fetch the site X data and do something like server side rendering and return me an exact replica of site X which I will load in my webapp. When user will navigate on site X and route to any path, that route will be sent to same API and now the response will be replica of baseURL/data/route. Even though my API endpoint is now exposed instead of site X url in browser console, I am okay with this because that API is protected somehow and cannot be accessed as it is without proper authentication which I am doing in my case on login page.
I want to get more thoughts on this entire scenario and possible alternatives. Also, if anyone can suggest if/how it is possible to write such an API. I can think about changing AngularJS or C# as technologies in my project but definitely that's not the most go-to-solution for me. Any suggestions and thoughts are welcome!
Edit: I have created a site using a static site generator. I want my users to be able to access that site only if they are 'my users' i.e. authenticated by my APIs. But, that generator is not allowing me to integrate my API and neither I can get the static assets like .html, .css and .js files that I can host on my own server and integrate my authentication API. Hence, I had to think about a workaround like this.
I follow a forum where people post many links. I've made a script, using javascript and tampermonkey (chrome extension), that first opens all unread thread in new tabs, than it looks for unread posts that have links and opens those links in new tabs.
This script saves me a lot of time and it is working pretty well. Nevertheless, I would like to improve it.
I want to change the title of the page that has been just opened in order for it to show the name of the thread on which the link that sent to this page was. For example:
On the thread "Fruits" there is a link that opens a new tab to a website "www.apple.com". I would like to change the tittle of this page to "Fruits - The name of the page", so I can know from which thread this link comes.
I use localstorage and sessionstorage to send information from one page to another page when my script is navigating inside the forum domain, but I have no idea what I can use to send information across different domains.
Furthermore, I would like to know if there is a way to tell tampermonkey to run the script according to the link that was opened.
I do know how to change the tittle of the page, the problem is taking the name of the thread that is on the forum domain and using it to change the tittle on the new tab that is on a domain different from the forum domain.
Localstorage doesn't allow access on different domain. You need to store that data in database server. The easiest is to sign up a database service, and it provides API you can use. If you're going to host your own, you must write your own scripts that allow CORS to provide the access to database server.
is there a function in ColdFusion that detects whether or not a browser window is the top window? (Similar to (if (window == window.top)) in JavaScript)
The reason I ask is because I would like to make certain page elements present when the page is directly accessed by the user, and not present if my page is iframed.
CFML code runs on the CF server, whereas any considerations about browser windows obviously run on the client. CF is completely unaware of the UI configuration of the client system, all it sees is "a request". Indeed the requests don't even come from the client, they come from the web server which acts as a go-between for CF-serviced requests: CF has no interaction with the client itself.
The only information the web server gives to CF that in any way relates to the client browser is some of the stuff in the CGI scope, and obviously that's limited. And none of it relates to the configuration of browser windows / iframes.
You will need to solve this with Javascript (which I will add to the tags of your question).
To trigger different code to execute on CF given a certain browsing situation, you are going to need to use Javascript to add some information to the request to identify the situation to CF. This could be adding a parameter on the query string, or something like that.
If someone was 'wrapping' one of my products I'd want to know who and how so I could improve the experience for the user and the site owner. With that in mind, what I would do is automatically break out of any frames by default. I would then create a simple api and provide instructions to other webmasters on the proper way to include your content. Display different content once you've determined if your content is PROPERLY being included in another site. For webmasters that want to include your content:
Provide recommended height/width for the iFrame so you can
include your logo or ads with the content.
Provide anything you want them to include in the query string to help track usage.
You could even add fun stuff to your api to make your content look more integrated into the including website like reacting to url.bgcolor or url.bgimage.
You could go as simple as looking for and recording the value of some url variable like url.remoteSiteAddress or as complicated as registering the site and providing unique key. Of course there are other considerations to take into account to enforce the key. Being that you don't really care that the content is being displayed on a remote site, I suspect just recording a simple url variable is more your speed.
If a different website is putting your page in an iframe on their website, then you could use the CGI.HTTP_REFERRER variable to check if the website domain is yours or not, and load content as desired.
Not just faster but better both in speed and using resources/memory.
I have an app where I put in suggestions as user types in an input-type-text. So, should I use ajax to communicate between two pages or chrome.extension.sendRequest?
EDIT
I have a packaged app, all the app info is stored in background.html's websql database. there is an input-type-text on index.html (which opens when someone clicks on app logo form new-tab page)
The input on the index.html fetches suggestions from the database of background.html as the user types in it. (just like google does when you start typing the query on google homepage) I can fetch suggestion data in two ways, using XmlHttpRequest and through chrome's message passing api. Since there will be a lot of requests to the background.html, i was wondering which method would be efficient..
The app in question: https://chrome.google.com/webstore/detail/fddboknafkepdchidokknkeidnaejnkh?hl=en-US
EDIT 2
The app is 100% offline. I am not connecting to any server or website. I am not loading any external script, not even jquery. All the app ever connects to is the files in it's own directory. background.html loads when pc starts and index.html loads when user clicks the app icon in new tab page. To see everything in action, you can install the app and see it for yourself.
EDIT 3
I am using the regular ajax to call index.html from background.html and it seems to work fine. By the way, both index.html and background.html are in 'html' directory.
function ajaxcall(url,callback) {
var call = new XMLHttpRequest();
call.onreadystatechange=function() {if(call.readyState==4) callback(call.responseText);}
call.open("GET",url+'#'+Math.random(),true);
call.send(null);
}
ajaxcall('/html/index.html', function(response) {console.log('foo')});
You should use the message passing API whenever you want to communicate between content scripts and your other pages. I don't see how you want to use ajax here.
If you're communicating between let's say your background page and the browseraction popup you could also use chrome.extension.getBackgroundPage which simply returns you the window object from the backgroundpage. Due to the restrictions imposed on content scripts you can't use this function in content scripts. This means you will always have to use message passing api for content scripts.
Don't be fooled by the asynchronous nature of these message passing functions. it's just a design consideration from the Chrome team to use asynchronous functions (with a callback) everywhere. It doesn't mean they take a lot of time to execute. Although I haven't bench-marked them, They seem to execute almost instantaneously.
Edit
I misinterpreted your question. I thought you were asking about how to communicate between the different pages in your extension/app. What you are really asking is how to communicate with a web server.
The message passing API (sendRequest) only works for communication between different parts of your app. The reason why this API is in place is because different parts of your app run in different environments, these are called isolated worlds. These environments are completely separated from each other to protect you from malicious code. The only pinhole through these different environments is this message passing API.
If you want to communicate with a web server ('pages' as you call them) you will need to use ajax. With ajax you will also notice the tight security model of Chrome. Under normal conditions a webpage is only permitted to make requests to the same site it originates from. This is called 'same origin policy'. because your extension/app is not hosted (it is packaged) it has no rights to access a web server by default. You, as a developer, will have to request this right to the user. You can do this by filling in the permissions property in the extension manifest. Whenever the user installs your app he has to accept that you have the right to access a certain web server.
As I read your question I am assuming you are still not very familiar with Chrome extensions, apps, Ajax and policy in a browser. I encourage you to read further on these topics while you are developing your app so you can ensure the security of your users.
Edit 2
Ok, so you're communicating between two different parts of your app, the index.html and the background.html. For this you can use getBackgroundPage and directly call functions defined in your background page. What you do is the following: In your backgroundpage:
window.getSuggestions = function(query) {
// search database for query
return ['suggestion1', 'suggestion2'];
};
Then you can call the following function on your main page (index.html):
var backgroundPage = chrome.extension.getBackgroundPage();
var mySuggestions = backgroundPage.getSuggestions('suggestion');
console.log(mySuggestions);
It's that simple. You don't need any ajax or sendRequest for this. No asynchronous code at all. You can just call a function in a synchronous fashion and this will be returned:
['suggestion1', 'suggestion2']
I didn't benchmark this and I haven't seen any data about it yet but I'm pretty sure this code is a lot faster than the message passing API or ajax. I didn't know you could use XmlHttpRequest within your extension but if it's possible I expect it to be the slowest way since you're actually making a network call in stead of calling to in-memory objects. If you want to be sure about the timings I suggest you to benchmark them yourself. You can easily do this with Chrome by using the following code:
console.time('someID');
// perform heavy operation(s) here.
console.timeEnd('someID');
The time to perform this operation will be printed to the console.
Edit 3
Now that I think about it, I'm not really sure how you would do ajax between two pages inside an extension. Don't you need a web server for this task? Can you give an example in your question of how you achieved this in your app?