Facebook Connect - Single Sign On Causes Infinite Loop :( - javascript

I have a Facebook Connect (FBML) web application that has been live for a while now.
All is working well, however user's are reporting issues with the single sign on (and i have seen the issue on their computer, but have not been able to replicate locally).
With FBC, there is a "event" you can hook into to automatically determine if the user is authenticated to Facebook.
That is achieved via this:
FB.Connect.ifUserConnected(onUserConnected, null);
The onUserConnected function can then do whatever it needs to attempt a single sign on.
For me, what i do is a AJAX call to the server to see if the user has an active website account (based on the Facebook details - user id, which i store in my system when they connect).
If they do, i show a jQuery modal dialog ("Connecting with Facebook") and do a window.location.reload().
The server then kicks in, and does the Single Sign On. This code is executed on every page:
public static void SingleSignOn(string redirectUrl)
{
if (!HttpContext.Current.Request.IsAuthenticated) // If User is not logged in
{
// Does this user have an Account?
if (ActiveFacebookUser != null)
{
// Get the user.
var saUser = tblUserInfo.GetUserByUserId(ActiveFacebookUser.IdUserInfo);
if (saUser != null)
{
// Get the Membership user.
MembershipUser membershipUser = Membership.GetUser(saUser.UserID);
if (membershipUser != null && membershipUser.IsApproved)
{
// Log Them Automically.
FormsAuthentication.SetAuthCookie(membershipUser.UserName, true);
// At this point, the Forms Authentication Cookie is set in the HTTP Response Stream.
// But the current HTTP Context (IsAuthenticated) will read HTTP Request Cookies (which wont have the new cookie set).
// Therefore we need to terminate the execution of this current HTTP Request and refresh the page to reload the cookies.
HttpContext.Current.Response.Redirect(
!string.IsNullOrEmpty(redirectUrl) ? redirectUrl : HttpContext.Current.Request.RawUrl,
true);
}
else
{
HandleUnregisteredFacebookUser();
}
}
else
{
HandleUnregisteredFacebookUser();
}
}
else
{
HandleUnregisteredFacebookUser();
}
}
}
I have never experienced an issue with this, but user's are reporting an "infinite" loop where the dialog gets shown, the window is refreshed, dialog is shown, window is refreshed, etc.
Basically, my AJAX call is saying the user exists, but my single sign on isn't.
Which is hard to believe because the code is very similar:
This is the AJAX Web Service:
if (!HttpContext.Current.Request.IsAuthenticated) // If user is not yet authenticated
{
if (FacebookConnect.Authentication.IsConnected) // If user is authenticated to Facebook
{
long fbId;
Int64.TryParse(Authentication.UserId, out fbId);
if (fbId > 0)
{
tblFacebook activeUser = tblFacebook.Load(facebookUniqueId: fbId);
if (activeUser != null && activeUser.IsActive) // If user has an active account
{
return true;
}
}
}
}
So if the response of this WS is 'true', i do a window.location.reload();
So i have no idea what the issue is (as i cant replicate), sounds like the Single Sign On isn't adding the Forms Authentication cookie properly to the response stream, or the Response.Redirect(Request.RawUrl) isn't reloading the cookie properly.
How do other's handle this?
This is what should happen:
User logs into Facebook (on Facebook itself)
User comes to my site (which has been previously authorised)
My site compares the FBID with the FBID in my DB, and signs them in.
My site is an ASP.NET 4.0 Web Forms application, using the "old" Facebook Connect JavaScript API (FeatureLoader.js), and Forms Authentication.
The only other solution to an AJAX call/window.reload i can think of is an AJAX UpdatePanel.
Can anyone help me out?
EDIT
Also, i realise that i can also use 'reloadIfSessionStateChanged':true to do the reload (which stops the infinite loop), but the problem with this is i cannot show my nice fancy dialog.

So i found a couple of issues.
Firstly, i shouldn't be setting a persistent cookie:
FormsAuthentication.SetAuthCookie(membershipUser.UserName, true);
We should only do this when the user ticks a box such as "Remember Me".
Secondly, i was checking the FB Auth status on every page on the server, so this could have been getting out of sync in the client-side.
Here is my new solution - which is better, and has a failsafe for the dreaded 'infinite loop'.
I no longer check the FB Auth status on the server, i do on the client:
FB.Connect.ifUserConnected(onUserConnected, null); // runs on every page request, on client-side
In the onUserConnection function i do the following:
Call web service to see if user CAN be signed in automatically (same WS as above)
If ok, check a special "Single Sign On" cookie has not been set.
If it hasn't been set, redirect to FacebookSingleSignOn.aspx.
FacebookSingleSignOn.aspx does the following:
Signs the user in using Forms Authentication (same as above)
Creates a special "Single Sign On" cookie to signify a SSO has been attempted
Redirects to the homepage
So, at point 3 above - this is where the infinite loop "could" happen. (as the onUserConnected will run again)
But it is now impossible (i hope) for it to happen, as it will only attempt to do the SSO if they havent already tried.
I clear the SSO cookie on the Facebook Logout action.
Now works well, logic makes sense - and it's done on the client-side (where it should be, as this is where FB does it's magic).
Hope that helps someone else out.

Related

Refresh page on new user sign in when using Google oAuth 2.0

I'm trying to make a website using Google Sign For Websites. Mostly just for the sake of learning about how it works.
I've followed the steps outlined in that tutorial Google Provides which works fine. Users can successfully sign into my site, it is able to pass the users ID to the backend and then verify the ID server side using a php script.
Php then creates a session for the user on the website. What I can't figure out is how would I refresh the page when a user clicks the Google Sign in button and the sign in is successful. Refreshing the page would allow the home page to be reloaded with the new php session data.
<div class="g-signin2 signin-button" data-onsuccess="onSignIn" data-theme="dark"></div>
function onSignIn(googleUser){
// The ID token you need to pass to your backend:
var id_token = googleUser.getAuthResponse().id_token;
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://mywebsite.com/tokensignin.php');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('idtoken=' + id_token);
};
I've tried using simply location.reload() inside of the onload = function() portion of the code. This causes the page to just infinately refresh every time it is loaded however since Google verifys that the user is signed in through this xhr variable every time.
I've tried looking through their reference to use GoogleAuth.isSignedIn.listen(listener) to monitor any changes but it doesn't seem to fullfull what I want it to or I'm not using it correctly since I don't exactly know what the listener should be.
The other option might be to use their GoogleAuth.attachClickHandler(container, options, onsuccess, onfailure) function but I'm not entirely sure how the properly configure the options field/variable.
If someone could provide some insight as to how this world work I would greatly appreciate it.
To summarize if the user is already signed into my website using Google I want the page to do nothing, if they click the signin button, after the sign in is successful I want to refresh the page they are on.
You could add a listener to xhr with a callback function.
xhr.addEventListener("load", loginComplete);
And then create a function:
function loginComplete(evt) {
console.log("Login Complete");
window.location.reload();
}
EDIT:
Ok. Since the listener doesn't help. You will need to check if the user is already logged in. To save that information one thing I could think of would be using cookies.
So you could store the Auth Token you receive from Google and set a cookie for it and check everytime before you make your POST.
Here is a nice js cookie library: https://github.com/js-cookie/js-cookie
Then onload you save the id:
xhr.onload = function() {
Cookie.set('google_token', id_token);
window.location.reload();
};
And the onSignIn function would be like:
function onSignIn(googleUser){
var cookie = Cookie.get('google_token');
if(cookie != undefined){
//cookie is set
return;
}
...
//rest of the function
}
Of course you need to improve this code, for example, check if the token stored in the cookies is still valid, some cases you can just refresh it instead of making the user log in again (the oAuth API provide such things).
Make some security measures to be sure the token is not being injected and etc..
My approach was the same as yours and I indeed ran into the same problem with constant refreshing.
I tried a couple of solutions, the best working one was as follows:
User signs in using GAPI2 triggering callback onGoogleSignIn
Backend checks ID token validity, and signs user in through session on my webapp
If successful, I log the user out using GAPI2, preventing infinite reload, because onGoogleSignIn is never called again
Then I refresh the page and replace the sign-in button with a logout button for my webapp
This solution is of course pretty much useless if you want to manipulate some data on user's Google account, but I found no use for this functionality.
Code for signing someone out of their google account.
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
Why not have your back end redirect (or send info so the page so it can redirect on success)?
What is the need for reloading the login screen?
You could also check out the listener documentation it seems like this may solve what you want. ie. listen for user change and trigger a function or redirect.
https://developers.google.com/identity/sign-in/web/listeners

am using javascript to connect user login to my website

am using a JavaScript to connect users to the homepage after the processing page have finish loading and sent the user to the homepage of welcome.html but the fact is how do i end the session after the click the log out button, because after signing out and if they hit back they will still get back the welcome.html, i have try disabling the back button in the browser but that's not awesome, i just need to kill the session so that it won't get them back to the welcome.html after they sign out instead it goes back to login page and require them to sign back in to access the welcome.html, and in this fact am not using php or DB to connect the user login, am using javascript, i don't know if it could work maybe with php simple line of codes or tags.
Here is my JavaScript code, i use to connect the users:
function Login(FORM){
var done=0;
var username=document.login.username.value;
username=username.toLowerCase();
var password=document.login.password.value;
password=password.toLowerCase();
if (username=="jonson111" && password=="happy111") { window.location="HomeAccess_uche/processing.html"; done=1;}
if (username=="wilsonqaz" && password=="open123qaz") { window.location="HomeAccess_wilson/processing.html"; done=1; }
if (done==0) { alert("USERNAME OR PASSWORD IS NOT IN THE DATABASE PLEASE TRY AGAIN!"); }
}
am using dreamweaver and yes i know i will encrypt the Java codes so that users will not understand it, but i just need to end the session after they sign out, this have given me a hard time to figure out i have search everywhere in Google but nothing, anyone can help?
what ever you wrote is all about client side code. there is no point in worry about session , because you don't have a session at all. you are just using javascript. you don't have any server side code to handle session. anybody can see the user name and password by looking at your javascript code. More over once you redirect the page by window.location="HomeAccess_wilson/processing.html"; your " done=1; " and all the javascript variable will reset.
DISCLAIMER: This is a TERRIBLE IDEA. Your site will be completely unsecure. I am ONLY explaining a way to make it work so that a completely innocent, naive user would get the intended effect. Also, this will only work in modern browsers supporting local storage.
function save_login() {
localStorage.loggedIn = true;
}
function save_logout() {
localStorage.loggedIn = false;
}
Call those functions when they log in or log out, and then on every "secure" (but not really, this is totally unsecure) page you do
if (localStorage.loggedIn == false) {
window.location.href = "yoursite.com/login" //or whatever page you
//want to send them to
}

Google Plus api calls not authenticated

I'm developing a prototype with two simple pages and google plus integration. I have two pages, first one with a "login" button, the second one with a link. When the user clicks on the login button, I am calling:
var params = {"client_id":"<client_id>", "scope":"https://www.googleapis.com/auth/plus.login"};
gapi.auth.authorize(params, signinCallback);
The signinCallback looks like this:
var signinCallback = function(authResult) {
if (authResult['access_token']) {
gapi.auth.setToken(authResult);
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.list({
'userId': 'me',
'collection': 'visible'
});
request.execute(function(resp) {
console.log(resp);
});
});
} else if (authResult['error']) {
console.error('Sign-in state: ' + authResult['error']);
}
}
So when the user clicks the button, signs in and provides permissions to the app, I'm storing the token and making a people list call. This all works perfect.
My problem is when I navigate to the second page and try to make the same call I made before:
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.list({
'userId': 'me',
'collection': 'visible'
});
request.execute(function(resp) {
console.log(resp);
});
});
The call fails with the error: Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.
I thought when I did "setToken" after signing up originally, I wouldn't have to continue authenticating every single subsequent call, what am I doing wrong?
If these are truly two different pages (as opposed to one page that has made some AJAX or other calls to your server to get additional data), then each page has a completely different JavaScript environment. This means that the gapi object is a different copy on each page, and the authentication you've set on the first page hasn't been set on the gapi object on the second page. You are not setting a token for the session - you're setting it on a specific JavaScript object.
If you are using something like the Google+ Sign In, you could put the button on each page, and each page would get its own token when the user visits it, but this is somewhat inefficient, since it also means a round-trip to the server each time.
You could probably also do something like put the authentication token into temporary/session local storage, but you should be careful in this case that the token can not leak out and cause you a security issue.
There are other potential solutions, but it really boils down to how you intend to use the authenticated user as part of your client.

Facebook client side authorization flow and get access_token for all scenarios like when scope for an app changes, etc

I am trying to do 3 simple things using JS thru a client side authorization flow :
Check if the user is logged in. If not, display the login dialog with proper scope.
If user is already logged into Facebook on the browser, then ensure the app has been authorized (if not, display the Authorize dialog).
If user has already authorized the app then ensure that the access_token on the authResponse has the right scope (permissions) required by the app. (if not, redisplay the authorize dialog for the new permissions).
I am trying to leverage js sdk for this, without wanting to write any hand-coded dialog's etc, since the sdk handles the browsers/devices nuances automagically.
I could not find this properly described anywhere either on FB documentation or otherwise. (no documentation around the fact that the permissions/scope for the app can change post authorization).
Thanks you in advance.
After an arduous attempt I have this working and here are the steps to do this with the minimum amount of code.
*The Approach *
Use fb-login-button to facilitate login. Login Button is the only thing that is displayed when the user first arrives on the page. All other forms are 'hidden', forcing the user to click on this button before being able to go forward.
setup onlogin="function();" on the fb-login-button - so that you can hide the login-button and display the rest of the forms/etc for the user to proceed on your app. Please note that onlogin is called only once (either when the user logs in or authorizes the app with the new scope) - so it is safe to always hide the login-button and display the remaining of the page/form for the user to proceed.
The most important step : use the same fb-login-button to also do the authorization with the right scope : so even if the user is logged in - force the user to click the login-button. fb-login-button in this case, will check if the scope of existing access_token (for already logged in fb user), is a subset of that requested on the fb-login-button attribute. fb-login-button will display the authorize dialog for you automatically in this case. In any case the onLogin callback is triggered (when the login-button should be hidden() and the remaining page/forms be shown()).
Subscribe to the FB.events to get the authResponses and the corresponding access_token every time it is set, which happens 2 times :
a. When the fetch is first displayed for already logged in user
b. After the user authorizes the app first time or second time with the new scope.
This allows you to keep the authResponse and access_token fresh to be used for all subsequent graph calls/etc..
*Implementation Steps *
fb-login-button must be created with onlogin callback :
<div class="fb-login-button" onLogin="onLogin();" scope="publish_actions"
data-size="large">Use This App</div>
Please note that the text does not say Login, but 'Use this App', since the button is displayed for authorization in addition to just logging in.
Subscribe to the Events.
// authRepsonse Change is needed when the user hits the UseApp button
// and permissions are re-fetched - then a new token needs to be refreshed
FB.Event.subscribe('auth.authResponseChange', handleResponseChange);
// when the user logins and all permissions are all set, clicking on the
// use-app will not do anything and authResponse will always be null.
// so this subscription is necessary
FB.Event.subscribe('auth.statusChange', handleResponseChange);
Login Function to hide Login button and display form.
function onLogin () {
hideLogin();
showMain();
}
Handle Status Changes to save Token.
// global variable that contains the access_token
// for use in graph calls/etc.
var authResponse = null;
function handleResponseChange(response) {
if (response && response.authResponse) {
authResponse = response.authResponse
if (response.status == 'connected') {
updateUserInfo(response);
return;
}
}
else {
authResponse = null;
}
hideMain();
showLogin();
}
Hope this helps and I believe that this is probably the most optimal way of handling client side authentication flow without doing any extra authorization/login dialog and let FB sdk do the trick.

Strophe.js server sends unavailable status even for users actively chatting at the moment

I recently encountered another problem with Strophe.js application: Even though I am actively chatting with user B (sending proper xml according to the spec, including <active> and stuff) it happens that server informs user B that I become unavailable.
At the moment logic looks like this:
connect
send initial presence stanza
update status (away/dnd) when needed
start pinging server every 10s
user is able to chat now
Pinging is done in order to ensure server didn't crush, it's know and widely used technique (using <iq...>).
Every message sent by users looks like this:
<message from="me#server.ltd" to="friend#server.ltd" type="chat" id="randomID" xmlns="jabber:client">
<active xmlns="http://jabber.org/protocol/chatstates" />
<body>Text</body>
</message>
Do you know this problem and any possible solution for that?
This problem can be resolved by using Strophe.roster plugin. The issue is related to the fact that when user re-logged to the server new session was created. After some time (60s supposedly), the old session expired and server broadcasted info that the user is gone.
The solution requires storing resources created by each contact when they log in and removing them when user is gone. User can be assumed to be offline if and only if there are no more resources.
I also added new functions to the Strophe.roster eg:
isOnline: function (jid) {
var item = this.findItem(jid);
if (!item || !item.resources) {
return false;
} else {
for (var k in item.resources) {
return true;
}
return false;
}
}

Categories

Resources