Twitter: raymondcamden


Address: Lafayette, LA, USA

ColdFusion and OAuth Part 1 - Facebook

04-01-2013 17,752 views ColdFusion 51 Comments

Last week, for some reason, I had multiple requests for an example of ColdFusion and OAuth integration. I ended up creating quick demos for Facebook, LinkedIn, and Google. This week I'll be blogging each in turn in the hopes that these entries can help others. Today, I'm going to share the Facebook code.

Before I begin, I want to warn folks. I wrote this code very quickly. It is not optimized. Also, the person I was helping was on ColdFusion 8. So the code isn't exactly what I'd call up to date. Of course, it will run just fine with ColdFusion 10. I typically assume that folks take my code samples here as just that - code samples - but I wanted to be more clear that this code would probably not be exactly best practices.

To begin, ensure you have access to Facebook's Developer portal (developer.facebook.com) and create a new application.

You can call it whatever you want, but the name should reflect your site in some way. Users will see this when your app launches so make it familiar. You can ignore the other two options.

On the next page, make note of your App ID and App Secret:

Click on "Website with Facebook Login" and enter a value for your site. You can, and probably should, use a local domain. In other words, you can enter something under localhost. Obviously this will change to a production URL once you're done, but for testing, localhost is fine. For my testing I used: http://localhost/testingzone/cf8fb and clicked Save Changes.

That's it for the Facebook side. Now let's talk about the OAuth process in general. I'm not going to go very deep into this as OAuth has been discussed elsewhere and my focus here is to demonstrate a ColdFusion example, but at a high level, the process looks like this:

  • Your site tells the user that you're going to send them to Facebook to authenticate/connect.
  • You create a "special" link that includes some required crap in the URL. Along with the required crap, you will have some optional crap. So for example, many OAuth providers ask you to spell out exactly what you want to use from the user. I.e., how much private data you require. Your link will include that, and Facebook will then warn the user. I.e., "This site wants to take your lunch money, read your email, and have relations with your significant other."
  • The user clicks and ends up at Facebook.com with a app-specific screen there. See the previous bullet point on how that screen may change.
  • The user clicks Yes or No (or approve or whatever).
  • Facebook sends you back to your site. In the URL will be a flag that you can check that will tell you if the user allowed your app. If they did, you will also have a special code.
  • You take that code, make a request (using CFHTTP) to Facebook, to get a secret access token.
  • This access token then allows you to get stuff. What stuff depends on what you asked for (see bullet point two).

That's it - roughly - and what's cool is that you will see this exact same (for the most part) process over my next blog entries as well.

With that in mind, let's look at the code. Again, I want to warn you this is a bit rough. First, the Application.cfc:

Obviously the app ID and app secret are removed above. The redirect URL will be used in a bit. Note that I've enabled session management as well. Now let's look at index.cfm.

There's two parts to this template. The first portion is run when you first get to the site and haven't connected to Facebook yet. I'm using a simple Session variable to track that. I provide a quick prompt and when clicked, I send the user to Facebook. Upon reflection, how I did this was kind of stupid. I could have just had the link in the original HTML link.

Speaking of the link, note the parts to it. client_id comes from our application settings. redirect_uri simply tells Facebook where to send the user when they OK/deny the connection. As I said, it is perfectly ok for this to be localhost during testing. The state variable is a security setting. I use a Session variable to store a UUID and ensure that the remote site (in this case, Facebook) sends back the same state. I could also use it for other things, like, well, state. Imagine I had 2 areas of my site where you could connect to Facebook. I could use this as a way to say, "I was in products", or "I was in music." Finally, the scope references what I'm asking for in terms of user privacy. Check Facebook's docs for more on what you can set in that regards.

Ok, so what happens when you click? Here is a screen shot.

Now let's look at redir.cfm, the main handler for the result from Facebook.

We begin by assuming that Facebook has return a code variable to us in the query string. We also validate the state variable I mentioned earlier. At this point, we need to get the Access Token from Facebook. This is done with a simple CFHTTP call. Note we pass back in the redirect URI. We aren't going back to redir.cfm again, this is just part of the security system.

If everything worked out ok, the result will be a string that looks like this:

access_token=AAAX&expires=5183804

That's where the string parsing code above comes into play. The access_token is what will give us access to the user's data.

If you return to the code sample above for index.cfm, you will notice that I've got a Facebook component tied to the user session. I can pass in the access token there and make use of it for future calls. I wrote this component very quickly. It doesn't have nice error handling or even pagination, but you can see an example of getting my profile as well as my friends.

I didn't spend much time on their API, but it seemed pretty darn easy to use.

You may be wondering - during testing - how do I get that permission screen to show up? You want to go to your Facebook Privacy settings, and then Manage Apps. This can be confusing because there is another link for this that brings you to your apps as a developer. You want to ensure you come here via your privacy settings, which will then help you manage your user app settings. Here's mine:

See my test app there on top? If you click the delete icon (x), you get this prompt:

This sounds like it may delete the app completely, but to be clear, this is user specific only. It is safe to confirm and test your application again.

Anyway, I hope this helps. I want to be clear that there's more involved here than what I've shown, but I wanted to share my sample app in the hopes others could use it.

Related Blog Entries

51 Comments

  • Edward Rynearson #
    Commented on 04-01-2013 at 2:08 PM
    TY
  • Commented on 04-01-2013 at 2:10 PM
    YW :)
  • Commented on 04-01-2013 at 4:21 PM
    Great job on this Ray. Looking forward to reviewing the rest of the series.
  • Commented on 04-02-2013 at 3:50 AM
    great - just in time, I am working on a site that requires this stuff:)
  • Commented on 04-02-2013 at 10:49 AM
    Thanks for writing this series.. there is a project I've been itching to start using a site that utilizes oauth and I found it difficult to get started.
  • Commented on 04-02-2013 at 10:53 AM
    Part 2 was supposed to go live now, but I just blogged something else. It should be up around 4 though.
  • Commented on 04-02-2013 at 11:04 AM
    Great tutorial, thanks a lot :) I want connect this to database user - ie creating a registration form, is there any particular variable you would use to link the fb account to the database user? So they site can recognise them days later
  • Commented on 04-02-2013 at 11:08 AM
    Look at the result from the method called to get my own user details. It contains an ID. You could use that as a link to local tables. You may want to link it to username as well and ensure both match.
  • Commented on 04-03-2013 at 10:09 AM
    wondering if you are watching my google search keywords... reading my mind! thanks Ray.
  • jeff horne #
    Commented on 04-03-2013 at 5:15 PM
    Thanks. This is great. Looking forward to the rest of the series.
  • Balriam #
    Commented on 04-07-2013 at 5:49 AM
    Thanks
    looks too easy and any one can understand easly.

    Thanks
    Balriam
  • Commented on 04-09-2013 at 4:46 PM
    Hi Ray,

    Thanks for this I have managed to hook it into my site perfectly for a facebook login. One thing im struggling with though is getting it to work on my iphone (same script) it throws up an element state is undefined in session error. any ideas what might be going on here? beating my head off of my wall hasnt helped and has just made my mrs angry :)
  • Commented on 04-09-2013 at 7:42 PM
    Finally got a chance to sit down and try this code. Ray as always you're amazing in how you A) seem to be able to read my mind about what I want to research and B) always explain things in such a way that you make it look so easy. Great job as always and thanks.
  • Commented on 04-10-2013 at 7:32 AM
    Mike, one issue a friend of mine ran into was using the wrong domain in the redir. By wrong I mean going from www.foo.com to foo.com. This caused the CF session to reset. Any chance that is happening to you? I assume not since you said it works ok outside of iPhone.
  • Commented on 04-10-2013 at 11:11 AM
    As always Ray, you are a scholar and a gent!.

    That I think may be it, my redirect wasn't maintaining the www. and so it was dropping the session prior to firing the user to Facebook. forcing the www. seems to have resolved it nicely :)

    Thanks!
  • Steve #
    Commented on 04-17-2013 at 11:42 AM
    Ray,

    Thanks for the great post. I'm completely new to working with the Facebook API, and between your tutorial and the Graph Explorer I've been able to quickly and easily start getting data out of fb.

    https://developers.facebook.com/tools/explorer?met...
  • Gilles #
    Commented on 04-17-2013 at 2:54 PM
    Nice! Will you write one for Twitter too?
  • Commented on 04-17-2013 at 3:00 PM
    Wasn't planning on it. ;)
  • Commented on 04-22-2013 at 10:13 AM
    Fantastic - I just grabbed Linked In - very clean code - no useless files - works like a charm. I'll give this one a shot too. Great work.
  • Gary F #
    Commented on 04-23-2013 at 9:30 PM
    Thanks for the tutorial, Ray. It's taking between 4 and 6 seconds to execute the whole demo even when I run it again. Shouldn't it be taking about 1 sec? :-( BTW, when trying to submit a comment it kept telling me it's been marked as spam. I'm on my 12th revision...
  • Commented on 04-23-2013 at 9:38 PM
    Well, if you are logged in, the index.cfm page runs getMe and getFriends. Both of those are network requests that could be slow. You could wrap then with cftimer calls or just enabled debugging to verify. Typically you would cache those results and not rerun them every time.

    Sorry about the spam blocker going overboard on you.
  • Nathan Blixt #
    Commented on 05-01-2013 at 8:21 AM
    Works like a charm! However, what would be your method to automatically log in a user if their session expired but they're logged into Facebook? ...I might have found my answer: https://developers.facebook.com/docs/howtos/login/...
  • Commented on 05-01-2013 at 8:36 AM
    I believe if you autoforwarded them to the right link, they would auto bounce back.
  • Commented on 05-05-2013 at 10:21 PM
    For those that asked about Twitter Oauth I have put this together
    http://cfjquery.com/blog/post.cfm/login-using-twit...
  • Commented on 05-09-2013 at 1:32 PM
    Raymond, would you have any idea why index.cfm is throwing this error?
    Could not find the ColdFusion Component or Interface facebook.
    The error occurred in /web/cf.telegram.com/html/test/fb1/index.cfm: line 13
    13 :    <cfset session.fbAPI = createObject("component","facebook").init(session.fbaccesstoken)>
  • Commented on 05-09-2013 at 10:23 PM
    It means it can't "see" the facebook CFC. Did you copy it?
  • Commented on 05-10-2013 at 7:07 AM
    The Application.cfc file is in the same directory as index.cfm and redir.cfm. (Is there also a facebook.cfc?)
  • Commented on 05-10-2013 at 9:50 AM
    It is the last code sample above.
  • Commented on 05-10-2013 at 10:02 AM
    Gotcha. Thanks! (I REALLY appreciate it!)
  • Commented on 05-31-2013 at 3:35 AM
    Ray,

    Thanks for the great start. I tried to work through the example but I think the code for your redir.cfm code is missing. Any chance you can repost?

    Thanks,

    Seth
  • Commented on 05-31-2013 at 3:45 PM
    The Gist that had the code borked out for some reason. I made a new Gist and replaced it above.
  • Seth Johnson #
    Commented on 05-31-2013 at 5:02 PM
    Thanks Ray!
  • Commented on 07-05-2013 at 4:02 AM
    Hi Ray! Thanks for the great tutorial.

    I did a big and obvious copy and paste just to try and get your bear bones code working. When I login and get redirected back, I get the following error:

    key [STATE] doesn't exist in struct (keys:urltoken,sessionid)

    Seems state isn't defined in the session. Do you know how to solve this?

    I'm using Railo if that makes a difference?

    Thanks again!
    Mikey.
  • Commented on 07-05-2013 at 8:26 AM
    Hmm. Well, it could be that you are losing your session when you leave the site and hit FB. Did you perhaps wait too long to click back?
  • AJ Mound #
    Commented on 07-10-2013 at 9:53 AM
    Raymond,

    I've been looking for this for a while. Very useful! Was able to get this working very quickly.

    I'd like to ask an aggressively stupid question (as I've never dealt with a CFC in a session variable before): How does one invoke this from a .cfm? I see the dump, but I've been unable to use the invoke tag to parse out any of the information.

    Thanks
  • Commented on 07-10-2013 at 9:55 AM
    If session.foo was a component, then it would simply be:

    result = session.foo.someMethod()
  • AJ Mound #
    Commented on 07-10-2013 at 10:12 AM
    Wow, you responded in two minutes! Thanks, Raymond!
  • Michael #
    Commented on 07-29-2013 at 9:42 AM
    Hi Ray,

    I am still getting the error:

    key [STATE] doesn't exist in struct (keys:urltoken,sessionid)

    It seems that the session gets forgotten somehow. I might also add, that it IS initially set, because I am able to pass this to Facebook and then also have it returned in the URL.

    However, when it comes to comparing the value of url.state to session.state ...it hits the error. Session is not defined.

    Any idea how to solve this? I assume it happens because of a bug with cflocation?

    I am using Railo.

    Thanks,
    Michael.
  • Commented on 07-29-2013 at 4:50 PM
    Yes, it does sound as if the session isn't sticking. Can you look at the cookies in the request before you access the session variable? Maybe do a quick cflog before you go and when you come back and see if they are changing.
  • Commented on 08-08-2013 at 10:03 PM
    Hi. I've got this working on one of my sites, thanks!. When i try to run it on another subdomain of the site (same CF server, changed the FB appids etc) the session.state doesn't stick and bombs out on the redir.cfm page.. Any ideas? The sites area almost identical, sessionmanagement set as well. etc..

    thanks
  • Commented on 08-09-2013 at 2:41 AM
    @alessandro there's definitely an issue with CF when saving session variables and then doing a redirect. The session will be created and you can even test for it, but when you redirect and come back, it's gone! Happens to me on Railo. Seems like it's a common bug and intermitent at that. In theory, you don't NEED this state part, but it's more secure that way.

    I have founding saving session.state like so works better than saving it as part of a deeper nested session.

    For me, session.state works, but if I did, session.facebookStuff.state it does not.

    It's a weird one.
  • Adriano Azevedo #
    Commented on 08-19-2013 at 10:09 AM
    If facebook user made logout? How clean session or to verify if facebook user session is valid?
  • Commented on 08-19-2013 at 4:37 PM
    The call to login won't work if the session is expired. Nor would the call for user details.
  • AJ Mound #
    Commented on 08-21-2013 at 9:15 AM
    So, here's a fascinating error from CF/Facebook interaction. I created the world's simplest template that just outputs the URL variables sent to the page. This is the code:

    <cfoutput>#testVar1#</cfoutput><br>
    <cfoutput>#testVar2#</cfoutput><br>

    When I run it through my server, it behaves normally:
    http://myserver/facebook/wtf.cfm?testvar1=abc&...
    Returns: abc 123

    This is the same server that I use for my FB application. So, when it's accessed in the correct way via facebook, It appends a slash ("/") to one of the variables like this:

    https://apps.facebook.com/app_name/wtf.cfm?testvar...
    Returns: abc 123/

    Just curious if you think this is a result of FB messing with their server, or am I going crazy?

    Thanks,
    Andy
  • Commented on 08-21-2013 at 9:19 AM
    Not sure. Maybe they assume you aren't using URL params.
  • s. pradhan #
    Commented on 04-21-2014 at 9:13 AM
    Hello. This process was working fine till some user notified me that they are getting errors.
    Do you have any idea why this is happening all of a sudden?

    struct
    Charset    [empty string]
    ErrorDetail    I/O Exception: peer not authenticated
    Filecontent    Connection Failure
    Header    [empty string]
    Mimetype    Unable to determine MIME type of file.
    Responseheader    
    struct [empty]
    Statuscode    Connection Failure. Status code unavailable.
    Text    YES
  • Commented on 06-08-2014 at 1:57 PM
    isDefined(), in 2013, tut tut, Mr Cameron would have a field day with that :-)
  • Commented on 06-08-2014 at 2:28 PM
    Mr Cameron can suck it. ;) I don't get into the whole structKeyExists/isDefined thing. I just use what comes to mind first.
  • Commented on 07-01-2014 at 6:57 AM
    Great code, thanks. All of your code is working! Right now I'm trying to post a new post on my Facebook wall. I've put together the following code, but it is not giving the desired result. It's not posting and there is no error.

    Any thoughts would be great!

    http://pastebin.com/3PCYHrRK

    BTW in my Pastebin are 2 possible solutions. Neither of them is working...
  • Commented on 07-01-2014 at 7:34 AM
    It's getting better. I now realise I have to ask permission to publish by extending the scope to publish_actions and publish_stream. I've also changed the cfhttparam from URL to FormField. Still no luck though...
  • Commented on 07-01-2014 at 10:15 AM
    When you changed it to ask for the right permission did you get a better error message?

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty