Archive for January, 2012

Tutorial: Logging Into Facebook with Windows Phone 7 (Silverlight)

4 Comments »

So, you want to use Facebook to sign into something or in some way integrate Facebook with your Windows Phone 7 app. You are in luck because it is almost hilariously easy.

I’ve uploaded this tutorial as a zip file. I tried to github it, but I’m inching up on 1 hour of trying to figure out what the hell I’m doing wrong.

Download this Tutorial Project

I’ve tried to make this a full featured tutorial, so if you just want to get to the code, head down to the “Let the User Login To Facebook” section.

Get Your Facebook Key

Go to http://developers.facebook.com and log in with your Facebook account. Give them the permissions they need and go to the “Apps” tab. Click on “Create new App”

Fill in the App Display Name and the App Namespace and do the verification. Then you’ll get to a page that gives you your App ID and App Secret.

image

Don’t worry about anything else here… you don’t need to select anything in the “how your app integrates with Facebook” section to do this tutorial. If you’re using Facebook integration to do some basic login and simple posting, you could probably just use the Website option.

Also, click the “Graph API Explorer” on the left and keep that open. We’ll come back to that in a moment.

Add Facebook C# SDK

Download the Facebook C# SDK and extract it. Go to the “sl3-wp” folder and unblock the Facebook.dll file (right-click => Properties=>click “Unblock”)

Open up your project (for the example, I used the Visual Studio 10 “Windows Phone Databound Application” template) and right-click on “References => Add References…”. Click “Browse” and navigate to the Facebook.dll and add it to your project.

Build Facebook Login UI

We’ll do this quick here (no MVVM, no bindings) but in later versions I’ll integrate this into a more formal project.

Open your project in Blend. For this quick-and-dirty tutorial, we’ll just add another page title and another panel for the UI we want to show when the user is logged in and set the Visibility to Collapsed on those. Our visual tree should look like this.

image

In our loginPanel, we’re going to add a button and a WebBrowser to our panel. Set the button (containing “sign in using facebook” in the content) to the top and the WebBrowser to fill the panel. For a little flare, I’m going to add “ShowFacebookLogin” and “HideFacebookLogin” animations. My login screen now looks like this (the WebBrowser will animate in when we press the button).

image

When we get our data back, we’re going to tell the user it worked by showing their name and their Facebook avatar. So we’ll add a Grid with an Image and a TextBlock to display the user, along with a friendly “Hello”. (Make sure to name all these things so that we can update them from the code.)

OK… out UI is simple, but ready to roll. Now let’s do something when the user clicks the sign-in button. Go to the “Click” event and add a method like “StartFacebookLogin”.

image

Let the User Login to Facebook

Add “using Facebook;” to your references at the top of your MainPage.xaml.cs file. Now, add the following properties:

private FacebookClient _asyncFbClient;
private string _appID = "get from your facebook app page";
private string _appSecret = "get from your facebook app page";

Now go to your StartFacebookLogin method and add the following (explained at the end).

private void StartFacebookLogin(object sender, RoutedEventArgs e)
{
    string[] extendedPermissions = new[] { "user_about_me", "publish_stream" };

    var oauth = new FacebookOAuthClient { AppId = _appID, AppSecret = _appSecret };
    var parameters = new Dictionary<string, object>
                    {
                        {"response_type", "token"},
                        {"display", "touch"}
                    };
    if (extendedPermissions != null && extendedPermissions.Length > 0)
    {
        var scope = new StringBuilder();
        scope.Append(string.Join(",", extendedPermissions));
        parameters["scope"] = scope.ToString();
    }

    var loginUrl = oauth.GetLoginUrl(parameters);
    webBrowser.Navigated += new EventHandler<NavigationEventArgs>(CheckForAuth);
    webBrowser.Navigate(loginUrl);
}

Remember how I told you to keep the Graph API open? Here is why. It has a big list of all the extendedPermissions that you can tell Facebook you want from the user. Here, we’ve asked for basic user information and the ability to publish to the user’s stream. Ask for only the permissions you need. The user can see the details of your permission request and may reject it if you ask too much.

Next, we create our OAuth client using out AppID and AppSecret and create a Dictionary of parameters communicating to Facebook the details of our request (for example, setting “display”, “touch” tells Facebook that we want the mobile interface.

We write out permission request into our parameters and then get a login url, which we will direct to our webBrowser. Here is what we will should get.

image

At the point Facebook walks the user through their login and, when they are done, it hands us back the access token we’ll need to get the user info and let the user make posts through our app.

This is why we attached the CheckForAuth event handler to our webBrowser. When we navigate to a new page, we’ll check to see if we got an access token using this code:

private void CheckForAuth(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    FacebookOAuthResult result;
    if (FacebookOAuthResult.TryParse(e.Uri, out result))
    {
        if (result.IsSuccess)
        {
            IsolatedStorageSettings Settings = IsolatedStorageSettings.ApplicationSettings;
            if(Settings.Contains("MyFacebookAccessToken"))
                Settings["MyFacebookAccessToken"] = result.AccessToken;
            else
                Settings.Add("MyFacebookAccessToken", result.AccessToken);
            Settings.Save();
            _asyncFbClient = new FacebookClient(result.AccessToken);
            _asyncFbClient.GetCompleted += new EventHandler<FacebookApiEventArgs>(_asyncFbClient_GetCompleted);
            _asyncFbClient.GetAsync("/me");
        }
    }
}

If the Uri does contain a Facebook OAuth result and it is a success, we save the access token to our settings and then immediately use it to get the most basic user information.

The problem we have right now is that we have no class to structure the data that comes back. So get that all squared away.

First, right-click on “References” and select “Add Reference…”. Select “System.Runtime.Serialization”.

Next, add a class to your project (I added mine in a folder called “Models”) and name it FacebookUser.cs.

image

In that class, add the following code

[DataContractAttribute]
public class FacebookUser
{
    public FacebookUser() { }

    private string _id;
    [DataMember(Name = "id")]
    public string ID
    {
        get { return _id; }
        set { _id = value; }
    }

    private string _name;
    [DataMember(Name = "name")]
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

The sample project has a much larger (although still incomplete) version of this model. But this one will do for our purposes.

We’ll make our “GetCompleted” event handler (remember that?) look like this:

void _asyncFbClient_GetCompleted(object sender, FacebookApiEventArgs e)
{
    FacebookUser _fbUser = e.GetResultData<FacebookUser>();

    Deployment.Current.Dispatcher.BeginInvoke(() =>
    {
        fbName.Text = _fbUser.Name;
        BitmapImage bi = new BitmapImage(new Uri("https://graph.facebook.com/" + _fbUser.ID + "/picture"));
        fbAvatar.Source = bi;
        HideFacebookLogin.Begin();
    });
    _asyncFbClient.GetCompleted -= _asyncFbClient_GetCompleted;
}

What we’ve done here is shove our data into a FacebookUser model. Then we use BeginInvoke to bring ourselves back to the UI thread and set all the properties we want.

Finally, we start the animation that hides the login data and shows that our Facebook login was a success.

Boom!

image


“Operation Not Permitted” On IsolatedStorageSettings in Windows Phone

3 Comments »

This is really fast but it took me a while to figure out. I was getting the following on my recent project when I tried to save something using IsolatedStorageSettings:

“Operation not permitted on IsolatedStorageFileStream.”

And I couldn’t find anything about it other than a very generic “Looks like a threading problem” answers.

Long story short:

I was, in rapid succession, running the same save code twice. What I think happened was that the IsolatedStorageFileStream was still open and trying to save when I tried to get it to save again on another thread.

That’s my guess. I figured I’d toss up this short post on it before I forgot what the problem was.


HttpWebRequest (WebClient) And Tombstoning in Windows Phone 7

6 Comments »

My initial concern here was to deal with tombstoning when it comes to HTTP requests, because that issue has kicked my butt on more than one account. But the second thing you’ll find here is a simple pattern for HTTP requests and responses in Windows Phone 7.

Most of the .NET code I look at uses WebClient to do HTTP requests. But using WebClient can negatively affect the UI thread in Windows Phone 7, so we have to do something a little more like this:

   1:  public void MakeSomeHTTPCall()
   2:  {
   3:      var request = (HttpWebRequest)WebRequest.Create("http://my.awesomewebsite.com");
   4:      request.Method = "GET";
   5:      request.BeginGetResponse(new AsyncCallback(GetSomeResponse), request);
   6:  }

Easy enough, right?

Not so fast!

If the user starts a request and then hits the “power” button on their phone, this will cancel that request. How can we tell when this has happened?

Well, we handle the response to this request in the “GetSomeResponse” method, so let’s take a look at that.

   1:  private void GetSomeResponse(IAsyncResult MyResultAsync)
   2:  {
   3:      HttpWebRequest request = (HttpWebRequest)MyResultAsync.AsyncState;
   4:      try
   5:      {
   6:          HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(MyResultAsync);
   7:          if (response.StatusCode == HttpStatusCode.OK && response.ContentLength > 0)
   8:          {
   9:              using (StreamReader sr = new StreamReader(response.GetResponseStream()))
  10:              {
  11:                  // This result string below is going to be whatever I get back,
  12:                  //   be it JSON or XML or whatever
  13:                  string result = sr.ReadToEnd();                        
  14:              }
  15:          }
  16:      }
  17:      catch (WebException e)
  18:      {
  19:          if (e.Status == WebExceptionStatus.RequestCanceled)
  20:              MessageBox.Show("Looks like your request was interrupted by tombstoning");
  21:          else
  22:          {
  23:              using (HttpWebResponse response = (HttpWebResponse)e.Response)
  24:              {
  25:                  MessageBox.Show("I got an http error of: " + response.StatusCode.ToString());
  26:              }
  27:          }
  28:      }
  29:  }

OK… let’s walk through this.

First, we get our request from the MyResultAsync.AsyncState (line 3), which we’ use to get our response (line 6). Now we can get a HTTP status code (like “200 – OK” or “404 – Not Found”) from this response, right?

No, we cannot. If the response is something other than OK, it will trigger an error and we will jump to the “catch” with a WebException error (line 17).

If we’re coming back from a tombstone (or Fast Application Switching, which will also cancel the request), the WebException status will be “RequestCanceled”. We test for that case and do something special for it (depending on the app, we may want to automatically re-do the request to ensure that we don’t put the user out of sorts by an errant power button press). Otherwise, we know it’s a normal HTTP status code and we can deal with it that way.


Adding Cookies to a Windows Phone 7 HttpWebRequest

2 Comments »

I just spent a couple days on this and, in the end, I didn’t really figure it out. Someone else at my work (the brilliant Rylan Barnes) showed me the workaround for it.

So… you want to add a cookie to your Windows Phone 7 Silverlight HttpWebRequest. Guess what? It sucks to be you! Or, at least, it sucked to be me because I couldn’t find anything on how to do this that worked.

I was working on a project that requires the user to authenticate and, on authentication, sends back a token (unique to that user) to be attached as a cookie for further authentication. However, when I attached the cookie and made my next request, it made that request without the authorization header. Our server said “Hey, looks like you’re not authenticated, I will now forget about that last cookie because you obviously didn’t get it. Have a new one!”

First I’m going to show you things that don’t work.

Didn’t Work: Adding An Empty Cookie Container to the Request

public void DoSomeCRUD(string userName, string pass)
{
    HttpWebRequest request = System.Net.HttpWebRequest.Create("https://myurl.com") as HttpWebRequest;
    if (HasSavedCookieContainer)
        request.CookieContainer = SavedCookieContainer();
    else
        request.CookieContainer = new CookieContainer();
    request.Credentials = new NetworkCredential(userName, pass);
    request.BeginGetResponse(new AsyncCallback(GetMyResponse), request);
}

private void GetMyResponse(IAsyncResult MyResponseAsync)
{
    HttpWebRequest request = (HttpWebRequest)MyResponseAsync.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(MyResponseAsync);
    // this method save the cookie container to IsolatedStorage 
    // so I can attach it to the request the next time I open the app.
    SaveCookieContainer(request.CookieContainer);
}

The saving worked, the cookie container came back just as I saved it. And then… it didn’t work. It was as if the cookie didn’t exist.

Didn’t Work: Ripping The Token Out Of the “Set-Cookie” Header

public void DoSomeCRUD(string userName, string pass)
{
    HttpWebRequest request = System.Net.HttpWebRequest.Create("https://myurl.com") as HttpWebRequest;
    if (IsolatedStorageSettings.ApplicationSettings.Contains["MyToken"])
    {
        string myToken = (string)IsolatedStorageSettings.ApplicationSettings["MyToken"];
        Cookie c = new Cookie("MyCookieName", myToken, "[valid path]", "[valid domain]");
        request.CookieContainer = new CookieContainer();
        request.CookieContainer.Add(new Uri("http://myurl.com"), c);
    }
    else
        request.CookieContainer = new CookieContainer();
    request.Credentials = new NetworkCredential(userName, pass);
    request.BeginGetResponse(new AsyncCallback(GetMyResponse), request);
}

private void GetMyResponse(IAsyncResult MyResponseAsync)
{
    HttpWebRequest request = (HttpWebRequest)MyResponseAsync.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(MyResponseAsync);
    // this method looked for "Set-Cookie" information in the header
    // and the pulls out the token data by brute force and save the
    // token as a string
    if (ResponseHasSetCookieInHeader(response))
    {
        string rawToken = GetTokenFromResponse(response);
        IsolatedStorageSettings.ApplicationSettings["MyToken"] = rawToken;
        IsolatedStorageSettings.ApplicationSettings.Save();
    }
}

The save worked here too, but the same result… it was like the cookie didn’t exist.

This Worked: Adding the Cookie as a Header

Finally we added the token and the credential information as header items. This finally worked.

public void DoSomeCRUD(string userName, string pass)
{
    HttpWebRequest request = System.Net.HttpWebRequest.Create("https://myurl.com") as HttpWebRequest;
    if (IsolatedStorageSettings.ApplicationSettings.Contains["MyToken"])
        request.Headers["Cookie"] = "MyTokenName=" + (string)IsolatedStorageSettings.ApplicationSettings["MyToken"];

    request.Headers["Authentication"] = "Basic " + Convert.ToBase64String(StringToAscii(userName, pass));
    request.BeginGetResponse(new AsyncCallback(GetMyResponse), request);
}

private void GetMyResponse(IAsyncResult MyResponseAsync)
{
    HttpWebRequest request = (HttpWebRequest)MyResponseAsync.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(MyResponseAsync);
    // this method looks for "Set-Cookie" information in the header
    // and the pulls out the token data, saving it to Isolated Storage
    if (ResponseHasSetCookieInHeader(response))
    {
        string rawToken = GetTokenFromResponse(response);
        IsolatedStorageSettings.ApplicationSettings["MyToken"] = rawToken;
        IsolatedStorageSettings.ApplicationSettings.Save();
    }
}

For the sake of giving credit, here is the StringToAscii method for the Authentication, tweaked from this StackOverflow answer from Hans Passant

public static byte[] StringToAscii(string userName, string pass)
{
    string s = userName + ":" + pass;
    byte[] retval = new byte[s.Length];
    for (int ix = 0; ix < s.Length; ++ix)
    {
        char ch = s[ix];
        if (ch <= 0x7f) retval[ix] = (byte)ch;
        else retval[ix] = (byte)'?';
    }
    return retval;
}

Follow me: matthiasshapiro