How to allow facebook login from iFrame - javascript

My app is widget that is embedded into other website but the one of the most important features is login with facebook.
But now I have a problem.
I have iframe tag but also inside iframe I have:
<a href="http://roomtobid.com/social/login/redirect/facebook" class="btn btn-info btn-lg col-md-12" >Facebook</a>
and now when I try to click and login, just nothing happened.
Also when I look at browser console I get error:
Refused to display
'https://www.facebook.com/login.php?skip_api_login=1&api_key=17499948800008…m5xN3sZ3hrPQWQ44FYQPR1yPpsDwLuvoTP9jtZq6%23_%3D_&display=page&locale=sr_RS'
in a frame because it set 'X-Frame-Options' to 'DENY'.
How I can solve my problem?
Also I try to add target="_top" at <a> tag and that work but then send me to first to facebook and then as callback to original domain not back to iFrame.
Is there any way to solve this?
I see that zopim chat allow their visitors to login with facebook, but their facebook login is opened into new small window.

but then send me to first to facebook and then as callback to original domain not back to iFrame
Of course not.
It only redirects back to the address specified as redirect_uri - and that address has to match the app domain, resp. be specified as a valid OAuth redirect URI in the app settings.
The only thing you can possibly do, is put the URL of the page that the iframe is on, into your apps callback URL as a parameter - so that your app can redirect back there, after Facebook login has redirected back to your app.
You could also try and put it into the session - but that might be problematic if the user has multiple pages opened that embed your widget, plus session cookies and 3rd-party cookie blocking easily cause additional trouble.
And that still leaves your with the problem of getting the "parent" page's URL in the first place. The parent page would have to explicitly transport that information to your widget in the first place, like via a GET parameter that they append to your widget's address in the iframe src. (Either the URL directly, or maybe some sort of client identifier, that you then look up in your database.)

Related

oAuth flow of data from third party domain

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.

How to get a generated id stored in localStorage in an Iframe?

I'm currently struggling with authentication on an application. The thing is that I need to get some authentication id from another application that is not in my domain. This could be easily solved if I was able to make a post request to that application and get in return that authentication id (glbid in my case).
I am able to perform that post request, but that would only work for authentication using the other app form. Since they also provide facebook / gmail login, what I'm trying to do is to read the glbid generated on that page after the login is executed.
I also know that the glbid that I need is located in localStorage on that page.
What I'm currently doing is loading an Iframe on my page with the link of that application login page. After the login via Iframe, I was hoping that I'd be able to access localStorage.
After some research, I've found out postMessage. It would work fine for some Iframe that I could have minimal control, but since the link that I'm embedding to Iframe is not in any case under my domain, I'm stuck!
Here's how I access the glbid via chrome console on the application domain login page:
JSON.parse(window.localStorage['ca.vc.data.glbid']).value;
As a result, I get the glbid. But I want to do that via Iframe. How can I do that? Can someome please support me, or if that is not possible via Iframe, show me another way to reach the same result?

How to handle redirect response from a Single Page Application browser only webapp

I have Single Page Application, browser only webapp (javascript, no server side).
The user interacts with the application and sets some variables.
Far after the creation of the main index.html page, when the user wants to upload a file, he needs to authenticate with an external service using oauth2 (using "token / implicit grant").
Once authenticated, the authentication service responds with a redirect, possibly to another page (page2).
What should the page2 do to continue the flow of the program?
Should it redirect back to the original main page (this time, with the access token)?
If so, wouldn't this reload the main page and reset the application and the variables that were set?
Thanks,
Avner
EDIT
Ricardo,
I don't use a specific web platform, just plain javascript.
#charlietfl
I found a general example here that does postMessage between 2 seperate windows and it works ok.
I'm straggling to customize it according to Ricardo's comments, i.e.
make the second window popup
set a separate HTML file or a "noop" route on your SPA as redirect URL (what is a noop route?)
poll for that URL in the popup
I found this example that create the popup in the main page (index.html), set the redirect, and poll in order to close the popup.
In my case I set the redirect URL to a new page (auth.html) that does postMessage with the access token back to the main page (index.html).
If the auth.html page is not openned in the browser, should I still expect to receive a message in the main page index.html ?
From what I read for postMessage to work both pages, index.html, auth.html should be opened in 2 separate tabs in the browser.
You can use a small popup, which will load the necessary oauth2 endpoint, setting a separate HTML file or a "noop" route on your SPA as redirect URL. You may then poll for that URL in the popup, so you know when to close it (user is done with oauth2 flow).
This strategy is used by libraries like vue-authenticate, react-oauth and satellizer.
Also, depending on the specific service your are trying to authenticate against, you can find official libraries and SDKs that will handle the oauth2 flow automatically.
Last but not least, you could also save the current state of you application in browser storage, redirect to the oauth2 endpoint, and point back to your app as redirect URL. When coming back to your app, the specific route should be able to restore the saved data and resume user experience, with the addition of the oauth2 tokens.

Google API: Authorized JavaScript Origins

I'm implementing a Google+ Sign-In for our web service, and stumbled on "Authorized JavaScript Origins". Our clients have web addresses either as a sub-domain of our main domain, or as a custom domain name. Since the login page is under that sub-domain (or custom domain), and in order to make the Google+ Sing-In button work, that custom domain/sub-domain should be (manually) entered in the "Authorized JavaScript Origins" list (with both http and https).
Does anybody know a way to do that automatically (through some API maybe)?
If not, then how do you do it?
Not sure if there is an API for this. At first glance I don't see one. The alternative (aside from manually adding domains all the time) is to use a hidden iframe on each site - this iframe would come from your domain and would be the only thing that calls google services. The main sites would communicate with the iframe (postMessage) to tell it what to send google. This of course, opens up a security risk (anybody could load your iframe into their page and do bad things on your behalf) so you'll want to make sure that the iframe code refuses to do anything unless it's running within a page on a known-good domain.
You can also have a common URL which all subdomains point to when trying to log in with Google. Then have this URL redirect to your actual Google login path. Beats having to deal with an iframe this way.
Finally I made it to work, however there may be some fixes to apply.
So a server is host for many domain and subdomains (childs) which all of them needs google sign-in and there is a main domain (parent).
I implemented a general login page on parent which childs open this page via window.open() as popup. As client is in a popup, it is very likely that auth2 cannot open another popup, so the parent will do the google auth with {ux_mode: 'redirect'} parameter as gapi.auth2.SignInOptions.
Process will continue to your callback page which you provided as another gapi.auth2.SignInOptions parameter which is redirect_uri and is on parent.
On this page google may have provided you the golden id_token which you must authenticate this token on your server. And this was the main twist which you should use this information to create a token on your server which parent asked server to create, but send it to child on client side (for example via query parameter) to use it for later usage.
I will happily take any advice for security leaks or any comment which may ease the process just a little.

Facebook Javascript SDK Security. How do Facebook verify that the JS SDK is loaded in the right domain that specified in the app settings

There are few questions with same name but none of them intended to ask what I have in my mind. So we initialize FB js sdk with only app id. It's easy to know other web site's app id by looking at their facebook initialization source code. One might think that it's possible that a hacker might try to initialize FB JS SDK with other's app id and try to get their user access tokens. But facebook doesn't allow such stuff. You have to load js sdk from the same domain you specified in the site url property in Facebook Developer Apps page. So the question is how do they know that the jsonp whatever calls coming from the right client? It's not safe to that checking in client side since people can copy and modifty the javascript as they wish. So it has to be server side checking. I can only think of "referer checking" but I feel it cannot be considered a safe way.
Well, I'm not sure so this is only speculation..
First of all, when making an http request the HTTP referer header is added, and so when you load the sdk the url from which you're making the request is added as a referer.
Facebook can check on their servers where the request was originated from and compare that to what they have for the app settings.
It's possible of course to modify this header when making the request, which is why you don't get any error just by loading the sdk for an app if you're in the wrong domain.
The error will only occur when you try to interact with the sdk, for example trying to execute the FB.login method will open the auth dialog pop-up which will show the following error message:
An error occurred. Please try again later.
If you check the url of this auth dialog (which the sdk constructs) you'll notice these two query string parameters:
domain=THE_DOMAIN_OF_THE_PAGE
redirect_uri=FACEBOOK_URL which will contain origin, domain and relation=opener
What (probably) happens is that facebook checks the domain against the app settings, if it's ok it presents the user with the auth dialog, when he finished the process he is redirected to the redirect_uri.
Since the redirect_uri opens in the pop-up it can only communicate with it's opener if they are both in the same domain, a facebook domain which no one can have on his page other than pages served from facebook.
When the sdk loads it adds an iframe into the fb-root container which loads a facebook js which is loaded from the same domain as the redirect_uri, because of that the pop-up window can communicate back with the iframe and inform it with the auth response.
After the iframe got the response, the pop-up closes and the iframe informs the loaded sdk in the main page of the response.
I'm not sure which technique they use for that communication, but you can easy find more info about that by googling "cross domain iframe communication".
That's how I see it, but I can't be sure.
You can check the code for the js sdk # github if you want to really know what's going on.

Categories