30

I am developing a WPF application that requires me to get an Access Token from Facebook using oAuth. After much searching online, I came to the following conclusions:

  1. OAuth must be done in a browser
  2. I need to watch the URL posts in that browser, therefor it would have to be inside a WebBrowser WPF control

I decided to create a Modal Dialog for doing the Facebook authentication, and I can just take the Access Token and ignore the rest. I wanted to keep to the MVVM model but it was more difficult than I anticipated. Any ideas on how to do that would be very helpful

Here are some features that I implemented

  • Cookie deletion so could have another user authenticate without needing to log the current user out
  • Disable new account creation since it led to a weird UI experience
  • Listening to the cancel button from the javascript generated by Facebook

The WPF Window

The WPF is very simple. In essence it is just a WebBrowser control with the Navigated and Navigating events hooked up.

<Window x:Class="FacebookAuthenticator.FacebookAuthenticationWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Authenticate Facebook" 
        Height="600" 
        Width="600" 
        ResizeMode="NoResize" 
        WindowStyle="ToolWindow">
<Grid>
    <WebBrowser Name="webBrowser" 
                Navigated="webBrowser_Navigated" 
                Navigating="webBrowser_Navigating" />
</Grid>

The Code

//The Application ID from Facebook
public string AppID {get; set; }

//The access token retrieved from facebook's authentication
public string AccessToken {get; set; }

public FacebookAuthenticationWindow()
{
    InitializeComponent();
    this.Loaded += (object sender, RoutedEventArgs e) =>
    {
        //Add the message hook in the code behind since I got a weird bug when trying to do it in the XAML
        webBrowser.MessageHook += webBrowser_MessageHook;

        //Delete the cookies since the last authentication
        DeleteFacebookCookie();

        //Create the destination URL
        var destinationURL = String.Format("https://www.facebook.com/dialog/oauth?client_id={0}&scope={1}&display=popup&redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token",
           AppID, //client_id
           "email,user_birthday" //scope
        );
        webBrowser.Navigate(destinationURL);
    };
}

Getting the Access Token

I forgot exactly where I got this code (if someone can remind me so that I could give proper credit I would be grateful).

private void webBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    //If the URL has an access_token, grab it and walk away...
    var url = e.Uri.Fragment;
    if (url.Contains("access_token") && url.Contains("#"))
    {
        url = (new System.Text.RegularExpressions.Regex("#")).Replace(url, "?", 1);
        AccessToken = System.Web.HttpUtility.ParseQueryString(url).Get("access_token");
        DialogResult = true;
        this.Close();
    }
}

Deleting Cookies

I realized that after someone logged in, there status stayed that way and would not allow someone else to log in. I decided to remove the cookies in the beginning of each authentication in order to prevent this.

private void DeleteFacebookCookie()
{
    //Set the current user cookie to have expired yesterday
    string cookie = String.Format("c_user=; expires={0:R}; path=/; domain=.facebook.com", DateTime.UtcNow.AddDays(-1).ToString("R"));
    Application.SetCookie(new Uri("https://www.facebook.com"), cookie);
}

No New Accounts

Allowing the user to create a new account led to a weird UI experience. For my use cases, the user should already have an exsiting account. I disabled this by checking if the user was redirected to "r.php/" which is what Facebook uses to create a new account.

private void webBrowser_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
{
    if (e.Uri.LocalPath == "/r.php")
    {
        MessageBox.Show("To create a new account go to www.facebook.com", "Could Not Create Account", MessageBoxButton.OK, MessageBoxImage.Error);
        e.Cancel = true;
    }
}

Handling window.close()

The cancel button in Facebook's dialog shows the ability to close the window. I needed to catch this and make sure to close the window. I had no idea how but I saw that in the MessageHook I was able to see that the last message to be sent (int msg) each time was 130, so I just listened for 130. It's sloppy, but it works.

IntPtr webBrowser_MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    //msg = 130 is the last call for when the window gets closed on a window.close() in javascript
    if (msg == 130)
    {
        this.Close();
    }
    return IntPtr.Zero;
}

Summary

Using the code is pretty simple

FacebookAuthenticationWindow dialog = new FacebookAuthenticationWindow() { AppID = "YOURAPPID" };
if(dialog.ShowDialog() == true)
{
    string accessToken = dialog.AccessToken;
    //The world is your oyster
}

I hope this example is helpful to the community. I would really like to hear from anyone who has any improvements, insights or even criticism.

~Mo

Community
  • 1
  • 1
mkamioner
  • 2,451
  • 1
  • 17
  • 14
  • Is there an actual question here or is it all just an example? Posting a solution is fine but it should be done in a Q&A format where you separate out the problem from the solution and post the solution as an answer to the question. – Tim B Jan 15 '14 at 09:55
  • This question appears to be a solution not a problem. It should be edited and posted as a problem with the solution as an answer. – Tim B Jan 15 '14 at 09:56

0 Answers0