Revisited: Facebook, Safari and External iFrames that need cookies

Just thought I’d pass on the better solution I’ve come up with for dealing with the Facebook/iFrame/Cookie issue. The major problem with the last solution is, obviously, that it’s annoying to the user – they have to click something just because of some stupid technical issue! There’s also a more minor problem which is we are doing an AJAX request to test for cookies when it can be done for free on the browser. Anyway, I came up with a solution that fixes both of these issues, using javascript. Its a pretty good solution: no peformance drag on the server, no difference in user experience, and no clutter in your application code (its all in the view, baby (well, except for the tiny nessesary bit that carries over the session info into the new cookie)). Basicly, the script tries to store a cookie using the following function:

function Get_Cookie( name ) {

		var start = document.cookie.indexOf( name + "=" );
		var len = start + name.length + 1;
		if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) )
		{
			return null;
		}
		if ( start == -1 ) return null;
		var end = document.cookie.indexOf( ";", len );
		if ( end == -1 ) end = document.cookie.length;
		return unescape( document.cookie.substring( len, end ) );
	}

Now the fun part. If the above function fails, we want to attatch the session data to the links, but in our case we want to do it as the links load (rather than waiting until the page loads and risking the user clicking prematurely or forcing them to wait). This was achieved using the extremely useful onDOMLoad script, by Aaron Barker (make sure you javascript_include_tag ‘ondomload’ !)
:

var sess_id = "#{session.session_id}";
	 if(!Get_Cookie('_session_id')) {
		zelph_onDOMload('a','AddSessionGrabberToLink(theTarget);');
	 }

Note that we are storing the session id in a variable: we’ll be using this later to pass the session along in the links. Now to be safe, we don’t want to just stick the session the url – that could be secure data in there. Far better to attatch an event handler to the onclick of the link that creates a form with an invisible text field containing the sess_id and then submits it. That is done in the following function, which is the meat of the javascript:

function AddSessionGrabberToLink(link){
		//grab the link and stick it as a param.  make the new link submit a form that posts the session_id
		var url = link.href;
		var url_with_redirect = "#{url_for :action => :grab_session_and_redirect}" + "?redirect_to=" + url;

		link.onclick = function() {
				var f = document.createElement('form'); f.style.display = 'none'; 				this.parentNode.appendChild(f); f.method = 'POST'; f.action = url_with_redirect; var m = document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_session_id'); m.setAttribute('value', sess_id); f.appendChild(m); f.submit(); return false;
			};      }

Finally, we’ve got to actually deal with that session id. We’ll already have a new cookie and session id by the time the user has clicked the link, so rather than try to use the old session, we just find the old session, copy over what we need, and then redirect:

def grab_session_and_redirect
	    oldsession = CGI::Session::ActiveRecordStore::Session.find_by_session_id(params[:_session_id])
	    session[:facebook_session] = oldsession.data[:facebook_session]
	    session[:user_id] = oldsession.data[:user_id]
	    session[:username] = oldsession.data[:username]
	    session[:ip] = oldsession.data[:ip]
	    redirect_to params[:redirect_to] and return
	  end

The above is the only code that makes it into your controllers. Not too bad, and the user experience is much better!

17 Responses to “Revisited: Facebook, Safari and External iFrames that need cookies”

  1. Bailey Cross Says:

    i’m trying to embed a google map into a facebook application. majority of google maps use call to onload in body tag. Facebook does not support the body tag. Any ideas how to get it to work without using the body tag.

    thanks,
    Bailey

  2. digidigo Says:

    Baily — I made the decision to use an IFrame for google map integration. Works well and you can’t even tell for my application.

  3. Facebook, Safari and External iFrames that need cookies « Will Henderson Says:

    [...] I came up with a better solution that requires no user [...]

  4. jeremy Says:

    Thanks for the initial insight on how to fix the problem. I think we’ve come up with a slightly more elegant solution:

    So we check for our session cookie from the layout we use for Facebook. We defined a constant in ApplicationController. If the cookie doesn’t exist then do your form posting trick automatically. (note we don’t check for links and alter them on the page, and instead do this immediately when needed… should be only once per session. when we post to the controller it has sessions off since we already passed in a session_id and set the cookie there and then redirect to the initial page. Voila Safari iframes, cookie problem fixed.

    Again thanks for figuring out the problem!

    Inside your layout do:

    if (document.cookie.indexOf(“”) == -1) safariFixSession(“”, “‘fb/cookie’) %>”);

    Here’s the js function:
    function safariFixSession(sess_id, controller) {
    alert(‘no cookie found’);
    var url = location.href;
    var url_with_redirect = controller + “?redirect_to=” + url;

    $$(‘body’)[0].insert(”);
    var f = $(’safariFixSession’)
    f.method = ‘POST’;
    f.action = url_with_redirect;
    var m = document.createElement(‘input’);
    m.setAttribute(‘type’, ‘hidden’);
    m.setAttribute(‘name’, ‘_session_id’);
    m.setAttribute(‘value’, sess_id);
    f.appendChild(m);
    f.submit();
    }

    And here’s the controller:
    class Fb::CookieController < ApplicationController
    session :o ff
    def index
    cookies[COOKIE_NAME] = params[:_session_id]
    redirect_to params[:redirect_to] and return
    end
    end

  5. jeremy Says:

    Shoot sorry, that first JS line needs to be:
    if (document.cookie.indexOf(”COOKIE_NAME”) == -1) safariFixSession(”SESSION_ID”, “‘fb/cookie’) %>”);

    and remove the alert(”) line ;-)

  6. Tyson Says:

    I have tried this solution but I could not make it work. Do you have some sample code to share?

  7. ajay Says:

    This post is almost a year old. Is there now a standard way of dealing with the facebook-iframe-safari-cookie issue? Esp. for Rails?

  8. Sai Emrys Says:

    I’ve written this up in more readable form on my blog, here:

    http://saizai.livejournal.com/897522.html

    Thanks, Jeremy & Will!

  9. On the Pain of Developing for Facebook : Light Year Blog Says:

    [...] your Rails sessions to carry over from page #1 of your iframe application to page #2 and beyond. Will Henderson has a solution when your Rails application uses the ActiveRecord session store, but new Rails [...]

  10. Steve Madsen Says:

    That’s my trackback just above, but the summary doesn’t say enough.

    Will’s solution here is good if you’re using ActiveRecord-backed sessions, but newly generated Rails apps prefer using the cookie store. His trick won’t work for that, as there is no database row, so no row ID, so nothing to pass between the landing page and the rest of the iframe app.

    I describe a way to preserve Facebook’s fb_sig parameters through links to the next page, where now Safari will accept cookies since the user has interacted with the iframe.

    There is some other stuff in my post that may be of interest to Rails Facebook developers, too.

    http://lightyearsoftware.com/blog/2009/11/on-the-pain-of-developing-for-facebook/

  11. Mafia Wars Money Hack Says:

    Forget slaving away completing jobs on mafia wars for cash. With my mafia wars money cheats, all that becomes childs play. Literally, in 5 minutes, you can have as much cash as you want.

  12. Charlott Mcferran Says:

    I am so tempted to delete everyone who plays this stuff..it consumes my whole page and I am sure I miss updates I really care about…you should be able to turn off all updates from games..

  13. zynga poker Says:

    Myspace poker chips are generally truly like the majority of alternative virtual currencies relating to the internet, just way more fashionable. Zynga poker chips are used throughout virtual texas holdem poker applications found on social networking internet sites by online players just about all over the world anywhere from the United States, to Spain, to Germany. Gamers grind away within the online application after gathering up in distinctive tiered poker rooms which usually are structured according to the number of myspace poker chips they need to buy in. They could have fun at tables having buyins as low as one hundred, 1000, one thousand myspace chips all the way up to 100s of millions and in many cases billions of chips.

  14. Micheal Casaliggi Says:

    Thank you for this post, now ill go on this blog often and check for new articles.

  15. Pet Society Cheats Says:

    Thank you for this post, now ill go on this blog often and check for new articles.

  16. Fishville Cheats Says:

    I want to thank the blogger very much not only for this post but also for his all previous efforts.

  17. Bejeweled Blitz Cheats Says:

    I want to thank the blogger very much not only for this post but also for his all previous efforts.

Leave a Reply