Comments, Code and Qt. Some words about the wonderful world of software engineering

21May/1110

OAuth2 explained with Qt Quick

...or how to do Facebook authentication with Qt Quick.

In this blog post I will give you an idea how OAuth2 works and how to gain access to Facebook from your Qt Quick application using OAuth2. I could have used any other service that uses OAuth2 for this blog post too, but I chose Facebook since it is a fairly well known and widely used service.

To do OAuth2 authentication might sound daunting and I don't blame you. OAuth1 is hard to do. Still, it's used by many popular web services, like Twitter. Because OAuth1 is so hard to do, I've even created a library for Qt that handles OAuth1 authentication to make your life easier. But ever since the library got released I wanted to add support for OAuth2 into the library too, because I knew people also wanted to use services that only support OAuth2, like Facebook. But this was before I understood how simple OAuth2 really is. Now I am not even sure if it's worth adding OAuth2 support to this library at all.

How OAuth2 works

I am not going to explain the differences between OAuth version 1 and 2. Instead I will dive right into how to do OAuth2 authentication against Facebook in Qt Quick. There are four authentication flows specified in OAuth2 depending on the type of application that wants to use it (web applications and PC clients can use different flows) and the level of trust the application expects to user to have in the application. Facebook supports two authentication flows; the so called authorization code flow and the implicit grant flow. The implicit grant flow is suitable for desktop and mobile applications, so that is why I will cover it here. But the differences between the authorization code flow and implicit grant flow are subtle so you could easily do either with Qt Quick.

The goal for each of these flows is to get an access token for your application. With this access token your application can get to the protected resources (i.e. Facebook data of a user). It will denote to the service that a particular user has granted access to a particular set of data in this service. The access token is simply appended to the URL that is used when requesting data from the service. So all you need to do is to retrieve the access token and you are good to go for some data from the service!

Here comes the fun part; OAuth2 is really all about opening a web page in your application to a specific URL with some specified variables, checking if either the success or access denied URLs get loaded and parse the URL of the success URL to receive the access token. That's it! Probably the only difficult part in your application is to figure out how to embed an HTML engine into it.  But we know this can be easily done with QML, so by the time you finish reading this blog post, I hope you want to start integrating Facebook into your Qt application :)

Made easy with Qt Quick

FacebookAuthWeb.qml:

import QtQuick 1.0
import QtWebKit 1.0

WebView {
    signal loadStarted(url address)

    onLoadFinished: main.loadFinished(url)

    onUrlChanged:  {
        loadStarted(url)
    }

    url: "https://www.facebook.com/dialog/oauth?client_id=128520100559699&redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token&scope=publish_stream,read_stream"


}

This is a normal WebView in QML that loads a Facebook URL which is the initiation for OAuth2.

  • https://www.facebook.com/dialog/oauth - This is the login dialog that the user will login with to Facebook. Facebook will check the login credentials. If the user is successfully logged in, he or she will be presented with a view allowing your application (identified with the client_id) to access the resources the application wants.
  • client_id - You have to register your application with Facebook on http://developers.facebook.com. On your application's page in Facebook, you'll find the App ID which is your client_id. Put it here.
  • redirect_uri - By the OAuth2 spec, this is your URL that the service will make a request after the authentication is done. This is perfectly fine for web apps to request access to your Facebook account if you, for example, want to publish your Flickr picture stream on Facebook. Luckily Facebook understands that for a PC or mobile client it doesn't make sense to make web requests to some external URLs, so they have specified a special URL that the PC or mobile client can use to notice that the authentication was successful. For Facebook, use http://www.facebook.com/connect/login_success.html here. Other services might have similar URLs, but you need to check this with your service.
  • response_type - We say token here to tell Facebook that we want to use the implicit grant authentication flow.
  • scope - This variable tells Facebook to which resources you want to get access to. Some data is available on the basic authentication level, which you get if you don't specify this variable at all, but for many types of data you need to explicitly specify that you request access to it. The Facebook Graph API documentation will tell you the values you can use to get access to data.

Note that we have a signal here, loadStarted(url address), that gets emitted when a new page is loaded. Let's connect that.

main.cpp:

QmlApplicationViewer viewer;
viewer.setMainQmlFile("...");

OAuth2Authorizer authorizer;
QObject *rootItem = viewer.rootObject();
QObject::connect(rootItem, SIGNAL(loadStarted(QUrl)),
           &authorizer, SLOT(onWebLoadStarted(QUrl)));

...
}

The slot onWebLoadStarted(QUrl) now gets called whenever a new web page is loaded in QML WebView (well, not every time :)). Now we just need to check for the URL in that slot. Remember, if Facebook opens its predefined URL for successful authorization, we know the user has logged in successfully to Facebook and granted us the access level we requested for. The best part is, the access token is part of the URL!

oauth2authorizer.cpp:

void OAuth2Authorizer::onWebLoadStarted(const QUrl &url)
{
   if (!url.isEmpty() &&
      url.toString().startsWith(OAuth2Authorizer::SUCCESSFUL_REDIRECT_URL)) {  // Check for the pre-defined URL
      QString accessToken = url.encodedFragment();           // Get the URL fragment part
      accessToken = accessToken.split("&").first();          // Remove the "expires_in" part.
      accessToken = accessToken.split("=").at(1);            // Split by "access_token=..." and take the latter part

      setAccessToken(accessToken);
   }
}

The code above will

  • Get the URL of a newly loaded page.
  • If the URL matches the one for successful authorization (remember we set it ourselves when loading the first page)
  • Find the URL fragment (it's the part in the URL appended by the character '#') in that URL
  • Find the variable access_token from the URL fragment.

Yep - that's it! Now I have the access token and I can request data from Facebook. The web requests on how to actually retrieve some data and in which format the data is returned are service specific and are outside the scope of this blog post.

In QML we can, of course, check for the same URL as above for an successful authentication and change the state of the QML page to do some fancy stuff when the user is logged in. In my example, I just reset the URL so it won't show any web content when the authentication is successful.

Demo time!

Demo

You can now use the access token with the Facebook Graph API to access Facebook data. I've done a small Qt Quick application that uses the Facebook authentication to show my Facebook updates in a simple ListView.

The application doesn't do anything fancy, but it was done in one evening :) And I think it really demonstrates how simple it is to access Facebook data from your Qt application. 

Source code

I've put the source code in a remote branch of kQOAuth called "oauth2", so if you like you can check out the complete source code for this demo in Gitorious: https://www.gitorious.org/kqoauth/kqoauth/trees/oauth2/examples/facebookauth.

Technorati Tags: , , , ,

  • Iaco Vb

    Awesome tut, great… Gave me back some hope for developing my own 4Sq app starting from scratch :P

    Btw, i see you’re using as redirecting URI a FB address for successful login… any idea on how you could make it on 4Sq?

    Thanks a lot! :)

    • http://www.johanpaul.com/blog/ Johan Paul

      The redirect URI is the kludgy part of OAuth2. Luckily FB has a hardcoded URI they provide themselves for mobile and desktop apps just for this reason. I haven’t looked at 4Sq so I can’t say if they have anything similar. Hope so.

      You can of course just give them any URI and hope they don’t care if it doesn’t work for real and check for your own URI in your web page loading routine for that URI you gave them.

      Good luck with your app! :) I guess the hardest part is to get some cute graphics ;)

      • Iaco Vb

        Ihih, yeah for the moment i just have to manage the account connection issue ;)

        I checked their “official” application, and used the same URI, which is actually just a dummy https page, at “https://it.foursquare.com/test”,  so i hope it could be enough….

        As far as the debug prints, i get the Set access token as:  “…..” so it may be enough…. We’ll see ;)

        Thanks for the reply and for the Tut ;)

  • Tokyobim90

    The bug you mention in this post is closed as invalid in the Webkit project, is that correct in your opinion?

    • http://www.johanpaul.com/blog/ Johan Paul

      Thanks for pointing this out. I added a comment to that bug in the WebKit Bugzilla. Let’s see what they think. It still feels like a bug to me, but I don’t know the internals of WebKit integration in QML :)

  • friend the file PARSER.H dont exist in the folder… you can provide? 

    • http://www.johanpaul.com/blog/ Johan Paul

      Hi!

      It must be the JSON parser that you are missing (and that I unsuccessfully) tried to upload as a subfolder to Gitorious.

      So go get it from here and put it into a subfolder as in my Gitorious example: http://qjson.sourceforge.net/

      • could you post the lines of code that takes the file to run your example? .. Please … really will thank you infinitely…

        • http://www.johanpaul.com/blog/ Johan Paul

          You mean how to get QJSON source code? Well, the line is actually on this page: http://qjson.sourceforge.net/get_it/

          So say “git clone git://gitorious.org/qjson/qjson.git” in the dir where my example is. You need to have Git installed on your system. In Linux it should be available in your distribution, but you might have to install it. In Ubuntu/Debian that would be “sudo apt-get install git”.

          The example should also run in Windows, and Git is available for Windows (http://code.google.com/p/msysgit/), but I have not tested my example in Windows. The .pro file could require some changes, but nothing major. The example is anyway just Qt Quick, Qt and using that JSON parser (for Facebook response parsing) that you can install in the same dir.

        • http://www.johanpaul.com/blog/ Johan Paul

          You mean how to get QJSON source code? Well, the line is actually on this page: http://qjson.sourceforge.net/get_it/

          So say “git clone git://gitorious.org/qjson/qjson.git” in the dir where my example is. You need to have Git installed on your system. In Linux it should be available in your distribution, but you might have to install it. In Ubuntu/Debian that would be “sudo apt-get install git”.

          Or you can actually also just download the source tarball for QJSON from here: http://sourceforge.net/projects/qjson/files/

          The example should also run in Windows, and Git is available for Windows (http://code.google.com/p/msysgit/), but I have not tested my example in Windows. The .pro file could require some changes, but nothing major. The example is anyway just Qt Quick, Qt and using that JSON parser (for Facebook response parsing) that you can install in the same dir.