The Run Around
From Facebook Developer Wiki
These are some notes I made by exploring the source of The Run Around. If there are errors or omissions, please fix.
Links:
- The Run Around - the demo site
- download the source code in .tgz form
- svn co http://svn.facebook.com/svnroot/platform/samples/therunaround/
Contents |
[edit] Database Structure
- users table: username (varchar(255), primary key), name, password, email, fb_uid, email_hash (varchar(64))
- runs table: id, username, date, miles, route
[edit] Authentication Process
See How Connect Authentication Works for how this works from the Facebook side. Here's how it works from the app's point of view:
[edit] Initial Page (index.php)
- determines if user is logged in by calling User::getLoggedIn(). If successful, $user is set.
- dispatches to "Front Page when logged out" (below) if $user not set
- otherwise, displays page with correct user info.
[edit] Determining if a user is logged in, User::getLoggedIn (lib/user.php)
- checks cookies (via getLoggedInNative) for indication that the user is logged into the site-specific auth mechanism ("native" auth). This returns a User object; store this in $native_user.
- asks facebook api to check cookies for indication that this user is logged into facebook, via facebook_client()->get_loggedin_user(). This returns a Facebook uid; store this in $fb_uid.
- Note that the Javascript facebook client scripts have to run and present a login/"agree to TOS" dialog to set up these cookies for the first time.
- This is not an asynchronous call to the Facebook Web service - the uid can be retrieved using only cookies.
- If $fb_uid is set, determines whether a user (in the users table) exists for the given Facebook UID.
- Takes action based on the following truth table:
| $native_user set | $fb_uid set | Found a User for $fb_uid | Action |
|---|---|---|---|
| 0 | 0 | 0 | Return false (not logged in) |
| 0 | 1 | 1 | Return the User found from $fb_uid |
| 1 | 0 | 0 | Return $native_user |
| 0 | 1 | 0 | Create a new User from $fb_uid and return it (see below) |
| 1 | 1 | 0 | Connect $native_user (User::connectWithFacebookUID) by updating its $fb_uid in the db, and return $native_user |
| 1 | 1 | 1 | Connect $native_user (User::connectWithFacebookUID) by updating its $fb_uid in the db, delete the old user associated with $fb_uid, and return $native_user. |
[edit] How to create a User based on $fb_uid (User::createFromFacebookUID in lib/user.php)
- searches for a user in the users table with a matching email address
- note: it actually checks the email hashes, not the address itself
- if found, just updates that user's fb_uid field and returns it.
- otherwise, makes up a new user object with name "FacebookUser_uid", no password and matching fb_uid.
[edit] Front Page when logged out (render_logged_out_index() in lib/display.php)
- displays a login form, which submits to login.php
- displays an FBConnect button, which is just an XFBML tag (defined in lib/fbconnect.php, render_fbconnect_button) with onClick bound to facebook_button_onclick()
[edit] facebook_button_onclick() (fbconnect.js)
- The button is created via XFBML's fb:login-button tag. So when it is clicked, it has code that runs within the JS client library internals. It calls FB.Connect.requireSession() (see below).
- ensures the facebook javascript stuff is initialised (ensure_init in fbconnect.js), which may asynchronously download scripts from Facebook's site, so pass a callback to ensure_init. More about it may be found (if you are lucky) at Using the Facebook JavaScript Client Library. This can be mostly ignored; most of the time the callback will be called immediately.
- calls get_sessionState().waitUntilReady() to set it up such that when Facebook considers the user logged in (has a session state), it will continue the function. See below.
[edit] callback function (fbconnect.js)
- Called when the Facebook backend has a session id ready (with a logged in user). The authentication process is done. If it worked (FB.Facebook.apiClient.get_session().uid has a value) then the page is refreshed and the whole process restarts, except that hopefully the login check will successfully find or create a user.
[edit] Detour: how requireSession() works (facebook js client library internals)
- examines the cookies to figure out which of these three states the session is in: not logged in; app not authorized (but logged in); connected (and authorized and logged in).
- if not logged in, displays a login dialog (via iframe (preferred), popup, or redirect) asking the user to login.
- if not authorized, displays a similar dialog, but only with the TOS links for facebook and the site.
- if connected, does nothing.
- Once the user finishes with the dialog, the iframe disappears, the Facebook session cookies are set for the site, and anyone who is waiting on the session (via get_sessionState().waitUntilReady()) is notified. This includes the above callback function.
[edit] Inviting Friends Process
[edit] Post Authorize Ping (fb_notification_receiver.php) =
- This URL is hit when Facebook notices that a user has authorized your app. (e.g., because they say "yes!" to friend invite)
- checks cookies to figure out the relevant facebook user
- examines the data given, including a list of usernames associated with that user that we provided in a previous step (find out?)
- for each username given, see if it exists in our database. If so, connect it with the facebook uid given by updating its $fb_uid in the db, and deleting any other existing users with that fb_uid.
