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. :)
Related
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).
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
First of all: I'm aware that there's plenty of questions about the same topic, but none of them did the trick for me (I've already been like 3 days trying to get this working)...
I'm working on a Javascript mobile game which includes some FB functions (through the Javascript Facebook API). I'm having trouble while trying to really log out from Facebook to log in with another user: everytime I log out, I expect to call the log in function and prompt the FB login dialog where I can specify my e-mail and my password, but instead of this, Facebook logs in automatically with the last user without even asking me for anything... I'm using CocoonJS as the mobile platform and plain Javascript (no jQuery):
Log in function:
CocoonJS.Social.Facebook.init({
appId:<<MYAPPID>>,
channelUrl: "channel.html"
});
var socialService = CocoonJS.Social.Facebook.getSocialInterface();
socialService.login(function(loggedIn, error) {
if (error) {
console.error("login error: " + error.message);
}else if (loggedIn) {
console.log("login suceeded");
// Ask for extended permissions
CocoonJS.Social.Facebook.requestAdditionalPermissions("publish", "publish_actions",
function(response)
{
callback(response.error ? false : true);
}
);
CocoonJS.Social.Facebook.getLoginStatus(function(response) {
if (response.status === 'connected')
{
CocoonJS.Social.Facebook.api(
"/me",
function (response) {
if (response && !response.error) {
// Getting "me" returns the information of the "same" user!
console.log("User: "+response.first_name+" "+response.last_name);
}
}
);
}
});
}
});
Log out function:
CocoonJS.Social.Facebook.getLoginStatus(function(response) {
if (response.status === 'connected')
{
CocoonJS.Social.Facebook.logout(function(response) {
console.log("User logged out!");
});
}
});
The console log is thrown, so it seems that the user has actually logged out, but when I restart the game, a kind of "I'm doing something" screen appears for less than a second and the same user who logged out is logged in again (even throwing their personal information through calling "me")... I was expecting Facebook to ask me with which user I would like to log in to my app, but it doesn't...
I thing it has something to do with "session" or "cookies", but I don't know how to clear them through Javascript (and, as I'm using CocoonJS as the "browser", I have little control over it)... Any idea?
Thanks in advance for your time and effort! :)
CocoonJS uses native iOS and Android Facebook SDKs. The SDK itself takes care of the session storage, no cookies are used in CocoonJS.
Facebook SDK has two different methods to close the session:
close(): Closes the local in-memory session object, but does not clear the persisted token cache.
closeAndClearTokenInformation(): Closes the in-memory session, and clears any persisted cache related to the session
CocoonJS.Social.Facebook.logout calls internally to closeAndClearTokenInformation on Android and iOS. It erases all the cached tokens, so the next login starts from scratch.
Facebook SDK uses the Facebook Application to handle the login process if it's installed on the device, otherwise it fallbacks to a webview based login. The problem may be that you are testing on a device with the Facebook Application installed, so the SDK is able to get the logged in user from the Facebook App and doesn't need to ask for it again. If you logout from the Facebook Application or you test your app on a device with no Facebook App, Facebook will ask for a user and password.
In my application, I implemented Google signout using jsapi.
I used the url https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=xxxxxx to connect to Google and then https://www.googleapis.com/plus/v1/people/xxxxxx to get user data from google profile.
Now I need to signout the user from Google while clicking a button from my application. How can I implement this in JavaScript, or at least it must ask the Google login page every time the user signs in.
I have tried approval_prompt=force, but seems not to be working.
Overview of OAuth: Is the User Who He/She Says He/She is?:
I'm not sure if you used OAuth to login to Stack Overflow, like the "Login with Google" option, but when you use this feature, Stack Overflow is simply asking Google if it knows who you are:
"Yo Google, this Vinesh fella claims that vinesh.e#gmail.com is him, is that true?"
If you're logged in already, Google will say YES. If not, Google will say:
"Hang on a sec Stack Overflow, I'll authenticate this fella and if he can enter the right password for his Google account, then it's him".
When you enter your Google password, Google then tells Stack Overflow you are who you say you are, and Stack Overflow logs you in.
When you logout of your app, you're logging out of your app:
Here's where developers new to OAuth sometimes get a little confused... Google and Stack Overflow, Assembla, Vinesh's-very-cool-slick-webapp, are all different entities, and Google knows nothing about your account on Vinesh's cool webapp, and vice versa, aside from what's exposed via the API you're using to access profile information.
When your user logs out, he or she isn't logging out of Google, he/she is logging out of your app, or Stack Overflow, or Assembla, or whatever web application used Google OAuth to authenticate the user.
In fact, I can log out of all of my Google accounts and still be logged into Stack Overflow. Once your app knows who the user is, that person can log out of Google. Google is no longer needed.
With that said, what you're asking to do is log the user out of a service that really doesn't belong to you. Think about it like this: As a user, how annoyed do you think I would be if I logged into 5 different services with my Google account, then the first time I logged out of one of them, I have to login to my Gmail account again because that app developer decided that, when I log out of his application, I should also be logged out of Google? That's going to get old really fast. In short, you really don't want to do this...
Yeh yeh, whatever, I still want to log the user out Of Google, just tell me how do I do this?
With that said, if you still do want to log a user out of Google, and realize that you may very well be disrupting their workflow, you could dynamically build the logout url from one of their Google services logout button, and then invoke that using an img element or a script tag:
<script type="text/javascript"
src="https://mail.google.com/mail/u/0/?logout&hl=en" />
OR
<img src="https://mail.google.com/mail/u/0/?logout&hl=en" />
OR
window.location = "https://mail.google.com/mail/u/0/?logout&hl=en";
If you redirect your user to the logout page, or invoke it from an element that isn't cross-domain restricted, the user will be logged out of Google.
Note that this does not necessarily mean the user will be logged out of your application, only Google. :)
Summary:
What's important for you to keep in mind is that, when you logout of your app, you don't need to make the user re-enter a password. That's the whole point! It authenticates against Google so the user doesn't have to enter his or her password over and over and over again in each web application he or she uses. It takes some getting used to, but know that, as long as the user is logged into Google, your app doesn't need to worry about whether or not the user is who he/she says he/she is.
I have the same implementation in a project as you do, using the Google Profile information with OAuth. I tried the very same thing you're looking to try, and it really started making people angry when they had to login to Google over and over again, so we stopped logging them out of Google. :)
You can log out and redirect to your site:
var logout = function() {
document.location.href = "https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=http://www.example.com";
}
For me, it works (java - android)
void RevokeAcess()
{
try{
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/revoke?token="+ACCESS_TOKEN);
org.apache.http.HttpResponse response = client.execute(post);
}
catch(IOException e)
{
}
CookieManager.getInstance().removeAllCookie(); // this is clear the cookies which tends to same user in android web view
}
You have to call this function in AsyncTask in android
To logout from the app only but not the Gmail:
window.gapi.load('auth2', () => {
window.gapi.auth2
.init({
client_id:
'<Your client id configired on google console>'
})
.then(() => {
window.gapi.auth2
.getAuthInstance()
.signOut()
.then(function() {
console.log('User signed out.');
});
});
});
I'm using above in my ReactJs code.
You can simply Create a logout button and add this link to it and it will utimately log you out from the app and will redirect to your desired site:
https://appengine.google.com/_ah/logout?continue=http://www.YOURSITE.com
just toggle YOURSITE with your website
This works to sign the user out of the application, but not Google.
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
Source: https://developers.google.com/identity/sign-in/web/sign-in
Ouath just makes the Google instance null, hence it you out of Google. Now that's how the architecture is made. Logging out of Google, if you Logout of your app is a dirty work, but can't help if the requirement stipulates the same. Hence add the following to your signOut() function. My project was an Angular 6 app:
document.location.href = "https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=http://localhost:4200";
Here localhost:4200 is the URL of my app. If your login page is xyz.com then input that.
this code will work to sign out
<script>
function signOut()
{
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
auth2.disconnect();
});
auth2.disconnect();
}
</script>
I hope we can achieve this by storing the token in session while logging in and access the token when he clicked on logout.
String _accessToken=(String)session.getAttribute("ACCESS_TOKEN");
if(_accessToken!=null)
{
StringBuffer path=httpRequest.getRequestURL();
reDirectPage="https://www.google.com/accounts/Logout?
continue=https://appengine.google.com/_ah/logout?
continue="+path;
}
response.sendRedirect(reDirectPage);
It looks like Google recently broke something with their revoke stuff (it's started returning 400 errors for us). You now have to call
auth2.disconnect();
In our case we then have to wait a couple of seconds for the disconnect call to complete otherwise the sign-in code will re-authorise before it's done. It'd be good if google returned a promise from the disconnect method.
If any one want it in Java, Here is my Answer, For this you have to call Another Thread.
1. Try this code, if you are using onSignIn() function
2.
<script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
<script>
function signOut() {
onLoad();
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
if(auth2.isSignedIn)
{
auth2.isSignedIn.set(false);
}
});
}
function onLoad() {
gapi.load('auth2', function() {
gapi.auth2.init();
});
}
</script>
I have an implementation for my website to have facebook single sign on, using their javascript sdk.
The javascript adds the cookie and I deal with it fine.
The question is related to when an user logs out of facebook, I would expect the auth.sessionChange or auth.logout events to fire, but that only occurs when the page is refreshed.
As my implementation is done server-side, this means that after the user logs out of facebook they can access one secure page one more time before being properly logged out.
Is this the normal case or do these events usually fire up straight away and I'm maybe doing something incorrect in configuration of the facebook app?
Almost 1/2 years later, i don't know if it's useful, but i try those things myself and observed that the subscribe solution doesn't work if i log in/out of facebook from their site(not from the current page that contains FB object, like using FB.login()).
Also by constantly polling getLoginStatus and getSession, nothing changes if i log in/out of facebook (!from their site).
My conclusion?! Their script is completely unsynchronized with the real events on facebook.
The only way that worked for me is to constantly do FB.ini(...).(it's like having a constant refresh)
I hope that it will be useful to other ppl having the same problem....?!?
The auth.sessionChange or auth.logout events are fired only if you use the code (got from FB developer page):
<div id="fb-root"></div>
<script src="http://connect.facebook.net/en_US/all.js"></script>
<script>
FB.init({appId: 'your app id', status: true, cookie: true, xfbml: true});
FB.Event.subscribe('auth.sessionChange', function(response) {
if (response.session) {
// A user has logged in, and a new cookie has been saved
} else {
// The user has logged out, and the cookie has been cleared
}
});
</script>
As you can see you need to use the event subscribe given by the FB js sdk otherwise the 2 events will never fire if you use them with other code (ex. jquery.bind())