Facebook API login flow for desktop application

When developing desktop applications that interacts with Facebook you have to implement the login flow your self. After the login flow completes you can use the normal Facebook SDK libraries by supplying it with the access token your received. When implementing the login flow you have to make sure that you receive the access you requested. You can get a partial approval by the user and not be able to access all the scopes you need. In this example I have implemented the Facebook login flow into a C# .Net desktop application. Full source is available for download.
Basics of the Facebook login flow
Most of the information about how to implement your own Facebook login flow can be found in Facebook’s developers article Manually Build a Login Flow. The basics for doing this from a desktop application is to send the user to the Facebook site for authentication in a web browser control. This is the same way a web application would be implemented but instead of using return (redirect_uri) that goes back to your server you send the user to https://www.facebook.com/connect/login_success.html instead. The returned information will then be added as url fragments. Url fragments are also know as bookmarks, the information behind the # in an url. At the same time any errors will be passed back as query string parameters, behind the ? in the url, so you have to check for that as well. Besides from that the implementation is pretty straight forward.
Prerequirements
Before you can send the user into the login flow you need to register a Facebook application. Use the “advanced setup” option to create it. After your created your application make a note of the application ID for later. Then go in to the application settings and select the “advanced” tab. Make sure that your set the following:
- Native or desktop app? – Yes
- Client OAuth login – Yes
- Embedded browser OAuth Login – Yes
Display the dialog
[csharp]
FBDialog fbd = new FBDialog({your-application-id}, {comma-delimited-list-of-scopes});
fbd.ShowDialog(this)
[/csharp]
This will show the Facebook login dialog. The implementation attached in the example code uses an extended version of the windows form web browser control. This extended version is used to be able to detect the javascript trying to close the dialog. The code for this extension is included in the example. Credit to Mrojas @ Artinsoft, the original code and article.
When the dialog loads the return url, mentioned above, and the comma delimited list of scopes are url encoded. Then we build the url and navigate to it.
[csharp]
string returnURL = WebUtility.UrlEncode("https://www.facebook.com/connect/login_success.html");
string scopes = WebUtility.UrlEncode(p_scopes);
FBwebBrowser.Url = new Uri(string.Format("https://www.facebook.com/dialog/oauth?client_id={0}&redirect_uri={1}&response_type=token%2Cgranted_scopes&scope={2}&display=popup", new object[] { p_appID, returnURL, scopes }));
[/csharp]
We also attach an event handler for when the browser navigates.
[csharp]
FBwebBrowser.Navigated += FBwebBrowser_Navigated;
[/csharp]
The login flow
Now the user is presented with the login screen and information about what application they will authenticate by logging in. The user have several options here:
- The user can click cancel at once and close the dialog. The FBDialog will then return DialogResult.Cancel.
- The user can login and authenticate your app but edit the scopes you are allowed to access. FBDialog will then return DialogResult.OK and you will be able to retrieve which scopes was approved. See below under “Return values”.
- The user can login and then click abort/cancel. The FBDialog will then return DialogResult.Abort. This will also be the result in case of an error. See below under “Return values”.
During the users interaction with the login flow the FBwebBrowser_Navigated function will fire every time the browser navigates to a new page/url. During this it will just set the title of the window to the same as the current document title until it hits the return url.
[csharp smarttabs=”true” tabsize=”4″]
if (FBwebBrowser.Url.AbsolutePath == "/connect/login_success.html")
{
// Check for error
if (FBwebBrowser.Url.Query.Contains("error"))
{
// Error detected
this.result = System.Windows.Forms.DialogResult.Abort;
ExtractURLInfo("?", FBwebBrowser.Url.Query);
}
else
{
this.result = System.Windows.Forms.DialogResult.OK;
ExtractURLInfo("#", FBwebBrowser.Url.Fragment);
}
// Close the dialog
this.Close();
}
[/csharp]
If an error is detected it will extract the error information, which is then available as public properties on the FBDialog object and return DialogResult.Abort. If the login is successful access token, expires and granted scopes will be available as public properties on the FBDialog object and it will return DialogResult.OK.
Return values
The dialog returns a standard DialogResult that can be used like this:
[csharp smarttabs=”true” tabsize=”4″]
switch (fbd.ShowDialog(this))
{
case DialogResult.Abort: // There was an error
break;
case DialogResult.Cancel: // User clicked cancel or closed the dialog
break;
case DialogResult.OK: // Logon successfull
break;
default:
break;
}
[/csharp]
If DialogResult.Abort is returned there are three public read-only properties available on the FBDialog object.
- FBDialog.error – name/type of the error
- FBDialog.error_reason – why the error was raised
- FBDialog.error_description – a brief description of the error
If DialogResult.Cancel is returned the user clicked cancel before logging in or closed the dialog.
If DialogResult.OK is returned the user logon was successful. However the user might not have granted you all the scopes you requested. There are several public read-only properties with information after a successful login.
- FBDialog.access_token – the access token string that can then be used for further requests against Facebook APIs.
- FBDialog.token_expires – a DateTime object representing the when the access token expires.
- FBDialog.granted_scopes – a comma separated list of scopes that the user granted you access to.
- FBDialog.denied_scopes – a comma separated list of scopes that the user denied you access to.
Conclusion
The implementation is pretty straight forward to use. The modification to the web browser control is only cosmetic but makes the flow work as users are use to in online apps. When you have received your access token you can use the Facebook SDK .Net for further interaction with the API. A great way to check out what you can do with the Graph API and what properties will be available on the objects is to use the Facebook Graph API explorer. Some scopes needs additional permissions from Facebook to use, but are all available to you for testing during the development process, more information is available on the Facebook Developers site.
This code can be used by anyone for any purpose but please link back and give credit!
Source code: [wpdm_file id=7]
Source code @ Github
Hey,
im just implementing facebook within desktop app, just on OS X (modified ios facebook sdk with ui elements changed). I`ve encountered weird problem with cookies crucial to keep user logged in are set to be session only (even after checking keep me logged in option), for just one session after logging in everything works fine. All settings seems to be same as yours. I was wondering if you had the same problem and if so how to solve this?
Thanks in advance
LikeLike
Hi,
Haven’t really thought about that since it’s a desktop application. So every time the user runs my application I want them to authenticate. How ever in my example I use the standard webbrowser control included in .Net and that caches just like IE would do. So the next time I run the application and send the user to the auth screen it will just flash by and I get the token back.
However this login flow is on a session basis. So if you need to keep the token alive for doing stuff in the background you might not be able to do that. In the past you could request an offline token that could be renewed at any time by your server or application to be able to access the API even if the user ended the session. I’m not sure if that is possible any more at least not with this login flow.
You can read more about different tokens here: https://developers.facebook.com/docs/facebook-login/access-tokens
/K
LikeLike
I am getting this:
You are not logged in: You are not logged in. Please log in and try again.
LikeLike
Have you created an app ID in the dev console?
LikeLike
Yes I have. My app ID is ‘477253152474540’.
I have also tried the following code samples:
This one works great but is browser based and requires Facebook JS SDK
http://blog.shakainteractive.com/fblogin/
This one is similar to yours (Using https://www.facebook.com/dialog/oauth?…) and it does not work either
http://www.codeproject.com/Articles/380635/Csharp-Application-Integration-with-Facebook-Twitt
Many thanks
LikeLike
Then it might be that this option has changed. Check this out: https://developers.facebook.com/docs/facebook-login/for-devices
LikeLike
I found the solution to the problem. Nothing wrong with your code.
Need to add this to Valid OAuth redirect URIs:
https://www.facebook.com/connect/login_success.html
Need to work on parsing the response contents though as its giving me errors.
Many thanks
LikeLike
Happy to hear it all worked out!
LikeLike
I think this API has been retired ;(
LikeLike
Thank you so much for this.. I wrote my own version , but was getting a bit frustrated with it. ( I was trying to do it in a background thread so I only had to show the browser when required..so I could find out if the user was logged in etc but failed!). It’s been a real bonus being able to re-use yours and delete all my mine 🙂
Only thing I added was to give an optional param to the ‘new’ called ‘rerequest’ ( I’m a VB coder sorry!) If set to true, it adds auth_type=rerequest to the auth logon URL.
Without this , there is no way to re ask for a scope that was previously rejected,
Thank you so much
Rob
LikeLike
I’m happy it helped! I will actually do a new implementation off this code in an upcoming project and I haven’t really reflected over the previously rejected scopes. You know why you are a VB coder? Because you can’t see sharp… 😉
LikeLike
Hah, I can do C# , but I always end up going back to VB. Been doing it for too many years (20ish). ( Plus I have too many other languages I work with every day , means I always go back to what I am most comfortable with when I get a choice ) .
I always get stick from C # coders though.. oh well, I’ll stay 2nd class 😛
I’ll have a look out for the new version 🙂
LikeLike
I started out as a basic programmer my self went through vb, several versions prior to .net. I fall back to it every now and then but juggling multiple languages the syntax in C#, java and python are more similar so it’s easier to stick to that. Thank you for taking the time to write a few comments, really enjoy getting the feedback!
LikeLike
Hello there,
We have been developing a WPF application. Some users are encountering this problem at facebook login section. I am looking forward to your help at
this point. Besides, those users who use any explorer version older than than Explorer 10 version cannot enter at all. Our most important problem right now
is the one that I mentioned below.
LikeLike
Hi, as far as I can see the second one you get is a success message. The error message doesn’t say much since it’s not in English. Make sure you have the proper return url configured for the Facebook app in the dev console.
LikeLike
After I login with my account, It thow an exception:
“Sorry, something went wrong.
We’re working on getting this fixed as soon as we can.”
LikeLike
I would suspect that it has something to do with the return URI settings.
LikeLike
Nice article, thanks!
How do I download the code? the link points back to this page.
LikeLike
The download link is dependent on javascript. Tested the link and it worked. Just to make it easier I put the code on Github as well. https://github.com/kallsbo/Facebook-API-login-flow-for-desktop-application
LikeLike
how can i log in with different accounts, after the first time the app logs in with my personal account, please help me!
LikeLike
You need to clear the cookies from the web browser control in order to get a “fresh” login page. If not the cookie will be sent to Facebook and it will just send the oauth token back since you have a valid session. Here you can read more about clearing cookies: https://social.msdn.microsoft.com/Forums/vstudio/en-US/2871993e-744e-46cf-8adc-63c60018637c/deleting-cookies-in-wpf-web-browser-control?forum=wpf
LikeLike
Hey Kristofer!
I get this::
Can’t Load URL: The domain of this URL isn’t included in the app’s domains. To be able to load this URL, add all domains and subdomains of your app to the App Domains field in your app settings.
any help would be appreciated
LikeLike
Did you setup the Facebook app as a native or desktop app? Sounds like you have a web app setup.
LikeLike
Hi,
I managed to do a login using this sample – many thanks for it!
But I ran into a problem: when requesting scopes, the buttons in those forms are not clickable. It works if pressing TAB to navigate to the button, then press ENTER.
Facebook support told me: “we don’t support this”…
More details: https://developers.facebook.com/support/bugs/307661020223552/
Is this a configuration problem on my side or a facebook bug?
Best regards
Wolfgang
LikeLike
This example had been around for a few years. I suspect that the procedures has changed when it comes to scoops. So you properly have to modernize the code somewhat to handle scopes.
LikeLike
Thanks for the reply.
In my real app, this is not an issue because the scopes will be defined in the facebook app definition.
But I wanted to post the issue here – maybe it helps others to work around the “seems to be not usable” form, and maybe someone has an idea. It might also be a configuration issue in my app.
Unfortunately, the WebBrowser control does not provide any methods to debug client side issues – it might be some JavaScript problem.
Best regards
Wolfgang
LikeLike