chrome.identity.launchwebauthflow fails for specific users - javascript

I've been researching this issue for quite some time now and have had no success. Hoping someone can shed some light on this!
I'm working on an Google Chrome Extension (browser action) where authentication/authorization is being done outside of Google (chrome.identity.launchwebauthflow with interactive set to true). We've had success with the authentication/authorization flow for some users but not all. Here are the interesting results:
User A clicks extension icon, clicks Authorize button, successfully gets auth code, exchanges it for access tokens, and can proceed with using the application.
User B clicks extension icon, clicks Authorize button, extension pop up closes. It fails prior to exchanging auth code for access tokens.
User B right clicks on extension icon, selects Inspect popup, clicks Authorize button, successfully gets auth code, exchanges it for access tokens, and can proceed with using the application.
User A starts up device in Safe mode with networking. User A clicks extension icon, clicks Authorize button, extension pop up closes. It fails prior to exchanging auth code for access tokens.
User A starts up device in Safe mode with networking. User A opens up a tab and loads the extension's url (chrome-extension://pathofextension). User clicks Authorize button, successfully gets auth code, exchanges it for access tokens, and can proceed with using the application.
Extension is converted to a packaged app. User A and B opens app, clicks Authorize button, successfully gets auth code, exchanges it for access tokens, and can proceed with using the application.
We think it's a client issue, but we're all using the same Chrome versions. What would cause the extension's pop up window to close when the auth code is being returned? We can't keep the developer console open to see if any errors appear because when the developer console is open it works just fine. We are using $.ajaxSetup({ cache:false }) to make sure caching is disabled for ajax requests.
Here's a snippet of the chrome.identity.launchwebauthflow call (originally called from the popup):
chrome.identity.launchWebAuthFlow({url:url,interactive:true},function(response) {
console.log('OAuth Response: '+response);
if (response) {
var authCode = encodeURIComponent(response.substring(response.indexOf('=')+1));
console.log('Auth Code: '+authCode);
userAuthorization(authCode);
} else {
authorizeButton();
}
});
Edited code after trying to apply the code-in-background solution:
Pop up script now calls background script:
chrome.runtime.sendMessage({type:'authorize',url:url},function(response) {
console.log(chrome.runtime.lastError);
console.log(response);
if (response && response.authCode) {
userAuthorization(response.authCode);
} else {
authorizeButton();
}
});
Background script responds to pop up script.
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse) {
if (message.type == 'authorize') {
var url = message.url,
authCode;
chrome.identity.launchWebAuthFlow({url:url,interactive:true},function(response) {
console.log('OAuth Response: '+response);
if (response) {
authCode = encodeURIComponent(response.substring(response.indexOf('=')+1));
console.log('Auth Code: '+authCode);
}
sendResponse({authCode:authCode});
});
}
return true;
});

Invoking launchWebAuthFlow from an extension popup is a very bad idea.
This operation is supposed to create a new window and focus on it. By Chrome UI conventions, this should close the extension popup - and with it, completely destroy the JavaScript context of that page. There will no longer be any callback to call.
This explains why "Inspect popup" helps - this prevents closing the popup on focus loss. There is no override for this mechanism outside of this debugging case.
This popup-dismissal behavior may subtly differ by OS, hence you might not have seen it on your development machine. But the convention is clear - any loss of focus should destroy the popup page.
The only truly persistent part of your extension that cannot be accidentally closed is the background script - that's where you should handle the chrome.identity authorization. Send a message from your popup code that requests it.
Update: Note that you can't return a response to sendMessage for the same reason - the popup no longer exists. Your logic should be to try to retrieve the token with interactive: false every time the popup opens - and if that fails, request the background to initiate the interactive flow (and expect to be closed, so no sendResponse).

Related

Logout when the user close the tab

On my Django site, I want to logout from the site automatically when I close the tab or browser. I mean when I close the site by closing the tab instead of using logout button, after entering the URL again, I want to see that the user has already logged out from the site to force the user to enter username and password again.
Is there a way to handle closing the tab in JQuery?
Thank you in advance.
Edit: onunload & onbeforeunload events cause to logout the user when also reloading the page.
Add your logout code to the on onunload event.
window.onunload = function () {
//logout code here...
}
In JQuery you can use the .unload() function. Remember that you don't have much time so you may send the Ajax request but the result may not reach the client.
Another trick is to open a small new window and handle the logout there.
window.open("logout url","log out","height=10,width=10,location=no,menubar=no,status=no,titlebar=no,toolbar=no",true);
If you want to disable closing the window (or at least warn the user), you can use this code:
window.onbeforeunload = function(event) {
//if you return anything but null, it will warn the user.
//optionally you can return a string which most browsers show to the user as the warning message.
return true;
}
Another trick is to keep pinging the client every few seconds. If no reply comes back, assume the user has closed the window, browser has crashed or there is a network issue that ended the chat session anyway. On the client side, if you don't receive this ping package, you can assume that network connection or server has a problem and you can show the logout warning (and optionally let the user login again).
You can use Javascript onunload & onbeforeunload events. In these events destroy the session cookie for Django.
Those events are also fired when you leave a site over a link or your browsers back button so be careful and think twice if this is really what you want.
I just ran into this. In your Django settings.py file, you can add the flag:
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
This will log the user out when the browser is closed. Note that the user will remain logged in if there are other tabs or windows still open (the browser must be completely closed).
window.onunload = function () {
//logout code here...
window.open("logout url","log out","height=10,width=10,location=no,menubar=no,status=no,titlebar=no,toolbar=no",true);
}
This code works.
this will open a small pop up window for the logout url.

Facebook Canvas, Unable to login from facebook on mobile devices

I have a canvas facebook application which has both a web page and a designated mobile page.
The web page works fine and also when simulating the browser to mobile with the console everything works fine.
But, when I try to run the app from the facebook mobile app the canvas app loads (which is correct), but it does not login.
I am using the FB.login function.
login: function () {
var deferred = $q.defer();
FB.login(function (response) {
if (!response || response.error) {
deferred.reject('Error occured');
} else {
deferred.resolve(response);
}
}, {
scope: 'email, user_friends'
});
return deferred.promise;
},
and in the settings > advanced - I have the:
Client OAuth Login,Web OAuth Login, Embedded Browser OAuth Login,Valid OAuth redirect URIs and Login from Devices filled correctly.
but still from the facebook mobile app the canvas app does not preform the login.
I have been trying to get this to work all day.
and I cant find a solution anywhere.
I also cant debug the mobile facebook app.
any ideas how to approach this issue?
EDIT
Also looked at my Node server logs and I see that the FB.login is not even called.
EDIT 2
I ended up replacing the login with getLoginStatus which poses no problem to me since its a facebook canvas app... but the question still remains on how to do the login.
EDIT 3 11/26/2015
well so getLoginStatus did not completely solve my issue since it does not in fact log the user in so for the canvas games you probably need to login for the first entry if you need permissions... my solution was to add the login if the getLoginStatus returns not_autorized like so:
/**
* [getLoginStatus get the FB login status]
* #return {[type]} [description]
*/
getLoginStatus: function () {
var deferred = $q.defer();
FB.getLoginStatus(function (response) {
if (response.status === 'connected') {
deferred.resolve(response);
} else if (response.status === 'not_authorized') {
_fbFactory.login().then(function (fbLoginResponse) {
deferred.resolve(fbLoginResponse);
});
} else {
deferred.reject('Error occured');
}
});
return deferred.promise;
},
But wait, there is more... the FB.login function will not work well on mobile canvas games (not sure if its just not triggered or the browsers blog the popups or both). anyway you need to actively call it via button... so for mobile canvas games I had to add a start playing button and then the login does work..
EDIT 4 (Final)
eventually I noticed that FB.login() does not get triggered unless its an external event that triggers it, so I had to make a change for Mobile canvas where if the getLoginStatus doesnt return connected then I show a login button which does the login... the rest stayed the same.
what I did for mobile was similar to the accepted answer only to suit my needs...
I hope this helps someone besides me...
Make sure you're calling FB.login() with an event triggered by the user, such as an onclick on a button, as browsers can block potentially unsafe/dangerous javascript that's called directly. This is an extra layer of security for the end-user. There's 2 ways to create a login button:
Generate a login button with facebooks login button generator:
https://developers.facebook.com/docs/facebook-login/web/login-button
The generated login button will look similar to this:
<fb:login-button scope="public_profile,email" onlogin="checkLoginState();">
</fb:login-button>
Create your own html and use an onclick event to call FB.init():
<button onclick="FB.init()">Login</button>
Notes from the Facebook developers website:
As noted in the reference docs for this function, it results in a
popup window showing the Login dialog, and therefore should only be
invoked as a result of someone clicking an HTML button (so that the
popup isn't blocked by browsers).
https://developers.facebook.com/docs/facebook-login/web
Also, FB.getLoginStatus is the correct first step in logging in to check whether your user is logged into facebook and into your app.
There are a few steps that you will need to follow to integrate
Facebook Login into your web application, most of which are included
in the quickstart example at the top of this page. At a high level
those are:
Checking the login status to see if someone's already logged into your app. During this step, you also should check to see if someone
has previously logged into your app, but is not currently logged in.
If they are not logged in, invoke the login dialog and ask for a set of data permissions.
Verify their identity.
Store the resulting access token.
Make API calls.
Log out.
I see that your game doesn't require a login anymore, but maybe others will find this answer useful. :)

Sending commands from server side (.NET) to browser

We have some web application that allows users login and do some work.
But sometimes users work with our web site opening more then one browser and this causes us a lot of problems.
How could we implement the following - on user log in to our web site, make automatic log out in all his previously logged in browsers?
Thanks a lot.
You can use JS localStorage on page load to detect a user login, the event will be fired on every tabs or window opened within the same domain :
function storageChange(event) {
if(event.key == 'user_login') {
// logout - except current window
}
}
window.addEventListener('storage', storageChange, false);
//when user logs in
window.localStorage.setItem('user_login', true);
That will work only if the user is using the same browser multiple times.

Check if user posted (or cancelled) sharing to Facebook in Share Dialog using FB.ui

I'm using a share dialog and I want something to occur after the user posts something to his/her timeline. Right now I'm using:
function shareToFB(myName, myLink) {
$('.overlay-bg').hide();
FB.ui({
method: 'share',
href: myLink,
name: myName
}, function(response) {
if (response && !response.error_code) {
alert("Something");
} else {
alert("Error");
}
}
);
}
But this causes "Something" to show up even when the user cancels posting. Is there any way I can find if the user has posted the message to his/her timeline without requiring any permissions. I won't mind if this requires me to use a different method of sharing (like a feed dialog).
Use Feed Dialog instead.
Despite its UI being ugly, successful sharing via Feed Dialog will return a response object like {post_id: "10206702999763274_10206703017843726"} regardless of user authenticating your Facebook app or not, while pressing cancel will return a null and closing popup will return undefined.
You can test this behaviour by going to your normal Facebook user profile settings and try removing your app from allowed list.
This is in contrast to the Share Dialog, which, if your app has not been authenticated by user, will return an empty response object {} regardless of successful sharing or not.
Furthermore, if you use the direct url redirect method instead of js SDK, when the user press cancel on the share page you will receive a GET parameter error_code and error_message appended to your redirect target.
[From December 2015] => Use Feed Dialog or the Share Dialog to access the post ID in the response. To achieve this the user must have logged into your app and granted it publish_actions. https://developers.facebook.com/docs/sharing/reference/share-dialog#response
NOTE: This is true for the time of this writing (API v2.3). However as Facebook is notorious for breaking API behaviour, you should test it yourself first.
According to the docs, the response is the object_id of the posted entry - and it is only filled if the user authorized the App. Meaning, you canĀ“t detect if a user really posted something if the user is not authorized.
Source: https://developers.facebook.com/docs/sharing/reference/share-dialog

Listen for event on new window

My application uses oauth to allow a user to login to Salesforce, and after logging in, they can access the application. What currently happens is:
The user clicks the "login" link, and is redirected to Salesforce
The user logs into Salesforce, and is redirected to the URL I specified
My server processes the request and redirects them to the home page
What I would like to do is this:
The user clicks on the "login" link, and a new window (window.open) with the Salesforce login page appears
The user logs in, and is redirected to the URL I specified
Once the server redirects to the home page, the home page fires a success or logged_in event in the window, which the original page listens for and interprets
This is what I've made so far (assume there is <button id="login">Log in</button>)
$('button#login').on('click', function() {
var popup = window.open('/auth/salesforce', 'login', '...');
popup.addEventListener('success', function() {
popup.close();
alert('Logged in');
});
});
and in the home page, I added to the section that displays when a user is logged in successfully:
var event = window.createEvent('loginSuccess');
event.initEvent('success', true, true);
window.dispatchEvent(event);
However, the success event is never fired. How would I fire a success event in the home page, to alert the original page that the user is successfully logged in?
Edit: I noticed there is the window.postMessage method, as referenced in the MDN docs. Is this what I should use? Or should I use another method to capture a successful login event on the newly created window?
I recently just put the same OAuth in our application. Don't try to use a new window to auth users just like all the social networks just let the page that the user is on go to the auth screen and then redirect you back... The only other way this can be done (and i'm not sure salesforce has this yet) is a javascript auth process.
Through my research, I found that the best way to accomplish this, at least if I don't need to support IE browsers, is to look at the window.opener value, and if it exists, run a function defined in window.opener. For example,
if (window.opener) {
window.opener.userIsLoggedIn();
}
window.close();
userIsLoggedIn() would be defined in the parent window that opened the new pop-up.
With that said, a couple caveats: in certain IE environments, window.opener will lose its value due to some security restrictions, thus making window.opener null and never firing the userIsLoggedIn() method on the parent window. In these cases, I would listen for the close event on the created window, and if the userIsLoggedIn() function hasn't fired, refresh the page. A second caveat is that you will only be able to call window.opener.userIsLoggedIn() if and only if it matches the Same Origin Policy; see the Wikipedia article on the subject. If this poses an issue and you cannot work your application around these restrictions, see this project for possible workarounds/hacks.
I don't have a need to support IE so I have not created this code, but if the window.onclose method fires in IE, listen for the window.onclose event and perform your checks after this happens.

Categories