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.
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.
Context:
There is a web app hosted at service.io, this is where most users would login to or create their account because it provides a whole boatload of services to the user. Users also have access to a widget which they can embed in their won website to provide their user's with the functionality from some of our services.
Problem:
Because the app and the widget will technically live in 2 different domains, sharing the authentication state is not possible. This means that whenever the users of the widget will have to login at the website where the widget is in, which is wrapped in an iframe to prevent anything from leaking. This is not ideal.
Current solution:
Since the problem is the fact that the widget would live in a different domain, I decided to host the widget under the domain where the app is (abc.com/embeds) and use the iframe's src prop to load that route (<iframe src="abc.com/embeds). To do this, I use what is known as multi-zones and rewrites via the NextJS framework (https://nextjs.org/docs/advanced-features/multi-zones. So the app would live under /* and the widget would live under /embeds.
The issue is now I have to use NextJS for the widget too, which is less than optimal because I don't use any Next-related features for the widget and therefore unnecessary increases the bundle size.
Ideal solutions (from my perspective):
The best and easiest would be if I could use the multi-zones + rewrites while hosting both applications on vercel but without using NextJS for the widget.
The only other solution that I could think of is to somehow synchronize the authentication states between the widget and app without hosting the widget at the app domain. I haven't implemented auth0 universal login myself, but I believe conceptually it's similar?
Questions:
How would I go about achieving either of the above solutions?
Is there a better alternative?
Security-wise is the current solution solid? Or should I make users login on every widget?
Visualization:
As was suggested by #cbr - You can use postMessage, Host Website (i.e your widget) will use postMessage to your app that is on service.io
To secure, do the following
Confirm origin when sending via targetWindow and evt.origin when receiving on your widget - This meets your need of confirming who you are sending to and the receiver is also confirming who sent the message.
If there are multiple targetWindow instances, simply name them and you can direct the corresponding message to the intended window.
Reference : https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
I'm writing a new JavaScript based Web app, which I need to secure in the following specific manner:
I should only allow for my app's index.html to be served if the request for it comes from a specific site.
By doing that, I will be forcing my users to go to that specific corporate site first, which will require for them to authenticate. Once the user is logged onto that site, they are provided with a link to my app. If my app's index.html is requested in any other way, beside following that corporate link, I would like to redirect the user to that corporate site.
How can this be accomplished?
You can use document.referrer to get the referral page. Once you get that from your corporate site you can probably put in some logic to redirect to the corporate site if it doesn't match where you're expecting.
Something tells me this isn't the best way to handle user authentication, but I'm new to that aspect.
Note: I'm on my phone, so excuse lack of code tags for that tiny bit up there.
Set a variable to the document.referrer().Then check the condition properly to determine if the referrer is in the proper page and if its not do a redirect..
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.
According to the Facebook API documentation, most of the work is handled through javascript.
That means that all the processing is done, and then the front end checks if the user is connected to Facebook/authorized. right?
My question is:
Suppose a user goes to my site for the first time ever.
He clicks on "facebook connect". The javascript verifies him as authentic, and it "redirects" to another page on my server. From then on, how do I know that the user is actually authenticated to my website, since everything is done on frontend?
I think this is correct, but aren't there some security issues..:
-After user clicks Login, Facebook redirects to a page on my site. AND they also create a cookie with a specific "Facebook ID" that is retrieved only from this user. My backened will "read" the cookie and grab that ID...and then associate it to my userID.
If that is correct...then it doesn't make sense. What if people steal other people's "facebook ID" and then forge the cookie? And then my backend sees the cookie and thinks it's the real user...?
Am I confused? If I am confused, please help me re-organize and tell me how it's like.
Facebook Connect uses a clever (or insane, depending on your point of view) hack to achieve cross-site communication between your site and Facebook's authentication system from within the browser.
The way it works is as follows:
Your site includes a very simple static HTML file, known as the cross-domain communications channel. This file is called xd_receiver.htm in the FB docs, but it can be named anything you like.
Your site's login page includes a reference to the Javascript library hosted on Facebook's server.
When a user logs in via the "Connect" button, it calls a function in Facebook's JS API which pops up a login dialog. This login box has an invisible iframe in which the cross-domain communications file is loaded.
The user fills out the form and submits it, posting the form to Facebook.
Facebook checks the login. If it's successful, it communicates this to your site. Here's where that cross-domain stuff comes in:
Because of cross-domain security policies, Facebook's login window can not inspect the DOM tree for documents hosted on your server. But the login window can update the src element of any iframe within it, and this is used to communicate with the cross-domain communications file hosted on your page.
When the cross-domain communications file receives a communication indicating that the login was successful, it uses Javascript to set some cookies containing the user's ID and session. Since this file lives on your server, those cookies have your domain and your backend can receive them.
Any further communication in Facebook's direction can be accomplished by inserting another nested iframe in the other iframe -- this second-level iframe lives on Facebook's server instead of yours.
The cookies are secure (in theory) because the data is signed with the secret key that Facebook generated for you when you signed up for the developer program. The JS library uses your public key (the "API key") to validate the cookies.
Theoretically, Facebook's Javascript library handles this all automatically once you've set everything up. In practice, I've found it doesn't always work exactly smoothly.
For a more detailed explanation of the mechanics of cross-domain communication using iframes, see this article from MSDN.
Please someone correct me if I'm wrong - as I am also trying to figure all this stuff out myself. My understanding with the security of the cookies is that there is also a cookie which is a special signature cookie. This cookie is created by combining the data of the other cookies, adding your application secret that only you and FB know, and the result MD5-Hashed. You can then test this hash server-side, which could not easily be duplicated by a hacker, to make sure the data can be trusted as coming from FB.
A more charming explaination can be found here - scroll about halfway down the page.
Same issues here, and I think Scott is closer to the solution.
Also Im using "http://developers.facebook.com/docs/?u=facebook.jslib-alpha.FB.init" there open source js framework. So things are a little different.
For me, via the opensource js framework, facebook provides and sets a session on my site with a signature. So what I am thinking is to recreate that signature on my side. - if they both match then the user is who he says he is.
So basically if a user wanted to save something to my database, grab the session signature set up by facebook and recreate that signature with php and validate it against the one facebook gave me?
if($_SESSION['facebookSignature'] == reGeneratedSignature){
// save to database
}else{
// go away I don't trust you
}
But how do you regenerate that signature? preferably without making more calls to Facebook?