Pulling Google+ data into Windows Store apps

If you caught the last GDL show for Google+, I demoed a few things I have been working on, most notably my Google+ demo for Windows 8. The following screenshot shows the app running within the Windows 8 Simulator:

As you can see, it’s a basic Windows Store app and just integrates a simple layout with the GridView control.  I’ll give you a brief overview of how the app works and how you could get started with our APIs in Microsoft’s new app model. This post will be focused on getting the relevant Google+ Data using the REST API.

The sample that was created for this code is available from here:

Please note that this is an early version of the sample, various best practices are not being followed but updates to the sources should address these in the future.

Authentication

A key concept demonstrated in this example is authentication.  Because the most exciting Google+ APIs from the perspective of Windows Store apps require that you be granted permissions by the user, you will probably be most interested in this step. Let’s go over how authentication will work for this app.

Authentication overview

This app is using the installed application flow so the following steps will be performed for authentication and API calls:

  1. Authenticate the user to get a single use access code
  2. Exchange the access code for a refresh token
  3. Use the refresh token to get an access token
  4. Use the access token to perform API calls

Some of the steps are made much easier because of new APIs for Windows Store apps.  However, I had trouble using the .NET client library because some of the referenced classes were deprecated for the new app model. I’ll be looking into what can be done to make the service generator build compatible libraries.

Without further adieu, let’s talk about data, starting with authentication!

Performing Authentication

The following code shows the code for the first OAUTH flow step, prompting the user for their credentials to get an access code:

        public async void Auth()
        {
            Windows.Storage.ApplicationData.Current.LocalSettings.Values["code"] = "";
            if (access_token == null)
            {
                if (refreshToken == null && code == null)
                {
                    try
                    {
                        String GoogleURL = "https://accounts.google.com/o/oauth2/auth?client_id=" + Uri.EscapeDataString(clientID) + "&redirect_uri=" + Uri.EscapeDataString(callbackUrl) + "&response_type=code&scope=" + Uri.EscapeDataString(scope);

                        System.Uri StartUri = new Uri(GoogleURL);
                        // When using the desktop flow, the success code is displayed in the html title of this end uri
                        System.Uri EndUri = new Uri("https://accounts.google.com/o/oauth2/approval?");

                        DebugPrint("Navigating to: " + GoogleURL);

                        WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
                                                                WebAuthenticationOptions.UseTitle,
                                                                StartUri,
                                                                EndUri);
                        if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
                        {
                            string response = WebAuthenticationResult.ResponseData.ToString();
                            // strip to start of auth code

                            code = response.Substring(response.IndexOf("=") + 1);
                            Windows.Storage.ApplicationData.Current.LocalSettings.Values["code"] = code;
                            // TODO: switch off button, enable writes, etc.
                        }
                        else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
                        {                            
                            //TODO: handle WebAuthenticationResult.ResponseErrorDetail.ToString()
                        }
                        else
                        {
                            // This could be a response status of 400 / 401
                            // Could be really useful to print debugging information such as "Your applicationID is probably wrong"
                            //TODO: handle WebAuthenticationResult.ResponseStatus.ToString()
                        }
                    }
                    catch (Exception Error)
                    {
                        //
                        // Bad Parameter, SSL/TLS Errors and Network Unavailable errors are to be handled here.
                        //
                        DebugPrint(Error.ToString());
                    }
                }
            }
            codeToAcccesTok();            
        }

What is going on in this code snippet is the WebAuthenticationBroker is used to perform the basic OAUTH flow.  The result of this flow is a “code” value. This code is a single use code that can be exchanged for a refresh token and an access token. Now that we have the code, it’s time to exchange that code for an access token and a refresh token. The following code snippet shows how that’s done using a HTTP Post method and the HttpClient class:

private async void codeToAcccesTok()
        {
            string oauthUrl = "https://accounts.google.com/o/oauth2/token";

            HttpClient theAuthClient = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, oauthUrl); 

            // default case, we have an authentication code, want a refresh/access token            
            string content = "code=" + code + "&" +
                "client_id=" + clientID + "&" +
                "client_secret=" + clientSecret + "&" +
                "redirect_uri=" + callbackUrl + "&" +
                "grant_type=authorization_code";

            if (refreshToken != null)
            {
                content = "refresh_token=" + refreshToken + "&" +
                "client_id=" + clientID + "&" +
                "client_secret=" + clientSecret + "&" +
                "grant_type=refresh_token";
            }

            request.Method = HttpMethod.Post;
            request.Content = new StreamContent(new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)));
            request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

            try
            {                
                HttpResponseMessage response = await theAuthClient.SendAsync(request);
                parseAccessToken(response);
            }
            catch (HttpRequestException hre)
            {

            }            
        }

In the above code snippet,  we setup an HTTP POST request and then send it to the Google authorization server with the parameters we need for authentication. The response data from the call contains a parameter for the access token as well as a refresh token. The following code snippet shows how the access token and refresh token are parsed from the response.

private async void codeToAcccesTok()
        {
            string oauthUrl = "https://accounts.google.com/o/oauth2/token";

            HttpClient theAuthClient = new HttpClient();

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, oauthUrl); 

            // default case, we have an authentication code, want a refresh/access token            
            string content = "code=" + code + "&" +
                "client_id=" + clientID + "&" +
                "client_secret=" + clientSecret + "&" +
                "redirect_uri=" + callbackUrl + "&" +
                "grant_type=authorization_code";

            if (refreshToken != null)
            {
                content = "refresh_token=" + refreshToken + "&" +
                "client_id=" + clientID + "&" +
                "client_secret=" + clientSecret + "&" +
                "grant_type=refresh_token";
            }

            request.Method = HttpMethod.Post;
            request.Content = new StreamContent(new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)));
            request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

            try
            {                
                HttpResponseMessage response = await theAuthClient.SendAsync(request);
                parseAccessToken(response);
            }
            catch (HttpRequestException hre)
            {

            }            
        }

        public async void parseAccessToken(HttpResponseMessage response)
        {
            string content = await response.Content.ReadAsStringAsync();

            if (content != null)
            {
                string[] lines = content.Replace("\"", "").Replace(" ","").Replace(",","").Split('\n');
                for (int i = 0; i < lines.Length; i++)
                {
                    string[] paramSplit = lines[i].Split(':');
                    if (paramSplit[0].Equals("access_token")){
                        access_token = paramSplit[1];                        
                    }
                    if (paramSplit[0].Equals("refresh_token")){
                        refreshToken = paramSplit[1];
                        Windows.Storage.ApplicationData.Current.LocalSettings.Values["refreshToken"] = refreshToken;
                    }
                }
                if (access_token != null)
                {
                    getProfile();
                }
                else
                {
                    // something is wrong, fix this
                }
            }
        }

In the above code, the parameters are extracted from the response data and parsed with a few simple string split calls. The refresh token is stored for later and the access token is cached for API calls. Because the parameters used for exchanging an access code for an access token/refresh token are very similar to those used when exchanging a refresh token for an access token, this method is reused later to get access tokens from the refresh token.

At this point, you have used the access code to get a refresh token and access token.  Now you’re ready to make authenticated API calls to Google+.

Performing authenticated API calls to Google+

In order to perform authenticated API calls to Google+, you need first to have gotten an access token.  This was done in the previous steps and is cached within the application. Next, you will make the API call using a HttpClient.  Finally, you will be serializing the objects from JSON. The first step we’ll discuss is specifying objects to be used later for JSON serialization.

Specifying objects for JSON serialization

Before you can parse JSON data from API responses, you need to specify the objects that will be parsed from the API call responses returned over REST. You can generate these classes from the discovery document. Because this was just an exploration of Metro and a demo, I hand crafted a few of the classes as opposed to writing a generator. The following example is the ActorClass and it’s child classes, which are returned within the activity feed and the people APIs.

        [System.Runtime.Serialization.DataContractAttribute()]
        public partial class NameClass
        {

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string familyName {get; set;}

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string givenName { get; set; }
        }

        [System.Runtime.Serialization.DataContractAttribute()]
        public partial class ImageClass
        {

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string url { get; set; }
        }

        [System.Runtime.Serialization.DataContractAttribute()]
        public partial class ActorClass
        {

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string id { get; set; }

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string displayName { get; set; }

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string nickname { get; set; }

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string tagline { get; set; }

            [System.Runtime.Serialization.DataMemberAttribute()]
            public NameClass name { get; set; }

            [System.Runtime.Serialization.DataMemberAttribute()]
            public string url { get; set; }

            [System.Runtime.Serialization.DataMemberAttribute()]
            public ImageClass image { get; set; }
        }

The classes and interfaces are specified using a shorthand that is common in C#/.NET for data contracts. Now that we have our objects for serialization, we’re ready to get the profile data and then serialize it to the actual objects.

Getting the user’s profile data

Here’s our favorite authenticated Google+ API call, getting profile data! First, we’ll need to specify the template that will be used by the JSON parser for storing the user’s profile object. This was described briefly in the above section.

Next, we’ll perform the RESTful query using the HttpClient.

        public async void getProfile()
        {
            httpClient = new HttpClient();

            var searchUrl = "https://www.googleapis.com/plus/v1/people/me/";

            httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + access_token);

            try
            {
                HttpResponseMessage response = await httpClient.GetAsync(searchUrl);
                ParseProfile(response);
            }
            catch (HttpRequestException hre)
            {
                DebugPrint(hre.Message);
            }
        }

Finally, parse the profile into the object we specified earlier.

        private async void ParseProfile(HttpResponseMessage response)
        {
            string content = await response.Content.ReadAsStringAsync();

            if (content != null)
            {
                var serializer = new DataContractJsonSerializer(typeof(ActorClass));
                thisUser = serializer.ReadObject(new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(content))) as ActorClass;
                ((MainPage)renderArea).RenderUser();
                GetActivities();
            }            
        }

You’ll now have an object, ActorClass, that has been populated with the information of the currently authenticated user.  If you want to inspect the object, put a breakpoint into the application and see what’s in there.  The following screenshot shows this in my project:

You can see the profile information for me in the object. This can be used later to render personalized data for the signed-in user. In my app’s case, the code behind just sets up the XAML objects based on the Actor object.

Conclusions

It’s really not that hard to run the REST flows from Windows Store apps. I think a logical next step for developers working on Windows 8 with Google+ is to implement a share contract because that would be really useful for users. Let me know if you have any questions about the sample! I’ll try and document the rest of how things work and how the UX works, but no guarantees because this is lower priority for me for now.

Finally, for y’all in the U.S., happy Thanksgiving!  I hope you’re having a great time with your family and friends.

See Also