← Back to overview
July 6, 2010 · Google Reader

Consuming Google (Reader) with .NET: Part 1 - Authentication

Introduction

Since a fewweeks I’ve become more and more a fan of Google Reader. This great online RSS reader supports starring items, extended search, … anything you expect from a good RSS reader. What I would like to do in the following articlesis connect to and consume Google services.

Before starting I do need to point out that there is a .NET ready API written by Google, called the Google Data Protocol. This library supports many of the Google services and is very well documented.

You can find it here: http://code.google.com/apis/gdata/

Downsides for me personally:

Also important to know is that there are other .NET libraries available to consume Google Reader:

The problem with these libraries is that authentication does not work (anymore). Google changed the way authentication is done. More info: http://groups.google.com/group/fougrapi/browse_thread/thread/e331f37f7f126c00

This solution:

Authentication with ClientLogin

If you want to authenticate a few things need to happen. A post made by xandy on StackOverFlow explains the process (points 1 to 3):

  1. Post to https://www.google.com/accounts/ClientLogin with login credentials.
  2. In return, three tokens will be passed if correct login: a. SID b. LSID c. Auth
  3. Save the Auth somewhere in application. Forget about SID and LSID (I guess they might remove them later on)
  4. In every request, add following in the header: headername:
    Authorization value: GoogleLogin auth={Auth string} e.g. (in java)

The following class does all this and returns the Auth token after a successful login:

public static class ClientLogin  
{
    /// <summary>
    /// Client login url where we'll post login data to.
    /// </summary>
    private static string clientLoginUrl =
        @"https://www.google.com/accounts/ClientLogin";

    /// <summary>
    /// Data to be sent with the post request.
    /// </summary>
    private static string postData =
        @"service={0}&continue=http://www.google.com/&Email={1}&Passwd={2}&source={3}";

    /// <summary>
    /// Get the Auth token you get after a successful login.
    /// You'll need to reuse this token in the header of each new request you make.
    /// </summary>
    /// <param name="service"></param>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <param name="source"></param>
    /// <returns></returns>
    public static string GetAuthToken(
        string service, string username, string password, string source)
    {
        // Get the response that needs to be parsed.
        string response = PostRequest(service, username, password, source);

        // Get auth token.
        string auth = ParseAuthToken(response);
        return auth;
    }

    /// <summary>
    /// Parse the Auth token from the response.
    /// </summary>
    /// <param name="response"></param>
    /// <returns></returns>
    private static string ParseAuthToken(string response)
    {           
        // Get the auth token.
        string auth = "";
        try
        {
            auth = new Regex(@"Auth=(?<auth>\S+)").Match(response).Result("${auth}");
        }
        catch (Exception ex)
        {
            throw new AuthTokenException(ex.Message);
        }

        // Validate token.
        if (string.IsNullOrEmpty(auth))
        {
            throw new AuthTokenException("Missing or invalid 'Auth' token.");
        }

        // Use this token in the header of each new request.
        return auth;
    }

    /// <summary>
    /// Create a post request with all the login data. This will return something like:
    ///
    /// SID=AQAAAH1234
    /// LSID=AQAAAH8AA5678
    /// Auth=AQAAAIAAAAB910
    ///
    /// And we need the Auth token for each subsequent request.
    /// </summary>
    /// <param name="service"></param>
    /// <param name="email"></param>
    /// <param name="password"></param>
    /// <param name="source"></param>
    /// <returns></returns>
    private static string PostRequest(
        string service, string email, string password, string source)
    {
        // Get the current post data and encode.
        ASCIIEncoding ascii = new ASCIIEncoding();
        byte[] encodedPostData = ascii.GetBytes(
            String.Format(postData, service, email, password, source));

        // Prepare request.
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(clientLoginUrl);
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.ContentLength = encodedPostData.Length;

        // Write login info to the request.
        using (Stream newStream = request.GetRequestStream())
            newStream.Write(encodedPostData, 0, encodedPostData.Length);

        // Get the response that will contain the Auth token.
        HttpWebResponse response = null;
        try
        {
            response = (HttpWebResponse)request.GetResponse();
        }
        catch (WebException ex)
        {
            HttpWebResponse faultResponse = ex.Response as HttpWebResponse;
            if (faultResponse != null && faultResponse.StatusCode == HttpStatusCode.Forbidden)
                throw new IncorrectUsernameOrPasswordException(
                    faultResponse.StatusCode, faultResponse.StatusDescription);
            else
                throw;
        }

        // Check for login failed.
        if (response.StatusCode != HttpStatusCode.OK)
            throw new LoginFailedException(
                response.StatusCode, response.StatusDescription);

        // Read.
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            return reader.ReadToEnd();
    }
}

That’s it. If you invoke ClientLogin.GetAuthToken you’ll get the auth token that can be used for each subsequent request.
You’ll also get correctly typed Exceptions depending on the error you get. If the exception is not know it’s just re-thrown.

The GoogleSession class

Before we can use the Auth token to make the request we need to take care of a few things:

That’s why we’ll use the following class that encapsulates all these features:

public class GoogleSession : IDisposable  
{
    /// <summary>
    /// Auth token.
    /// </summary>
    private string auth;

    /// <summary>
    /// Initialize request.
    /// </summary>
    /// <param name="auth"></param>
    public GoogleSession(string auth)
    {
        this.auth = auth;
    }

    /// <summary>
    /// Create a google request and get the response.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public WebResponse GetResponse(string url, params GoogleParameter[] parameters)
    {
        // Format the parameters.
        string formattedParameters = string.Empty;
        foreach (var par in parameters)
            formattedParameters += string.Format("{0}={1}&", par.Name, par.Value);
        formattedParameters = formattedParameters.TrimEnd('&');

        // Create a request with or without parameters.
        HttpWebRequest request = null;
        if (formattedParameters.Length > 0)
            request = (HttpWebRequest)WebRequest.Create(string.Format("{0}?{1}",
                url, formattedParameters));
        else
            request = (HttpWebRequest)WebRequest.Create(url);

        // Add the authentication header.
        request.Headers.Add("Authorization", "GoogleLogin auth=" + auth);

        // Get the response, validate and return.
        HttpWebResponse response = request.GetResponse() as HttpWebResponse;
        if (response == null)
            throw new GoogleResponseNullException();
        else if (response.StatusCode != HttpStatusCode.OK)
            throw new GoogleResponseException(response.StatusCode,
                response.StatusDescription);
        return response;
    }

    /// <summary>
    /// Create a google request and get the response stream.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public Stream GetResponseStream(string url, params GoogleParameter[] parameters)
    {
        return GetResponse(url, parameters).GetResponseStream();
    }

    /// <summary>
    /// Create a google request and get the page source.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public string GetSource(string url, params GoogleParameter[] parameters)
    {
        using (StreamReader reader = new StreamReader(
            GetResponseStream(url, parameters)))
        {
            return reader.ReadToEnd();
        }
    }

    /// <summary>
    /// Create a google request and get the feed.
    /// </summary>
    /// <param name="url"></param>
    /// <param name="parameters"></param>
    /// <returns></returns>
    public SyndicationFeed GetFeed(string url, params GoogleParameter[] parameters)
    {
        // Load the stream into the reader.
        using (StreamReader reader = new StreamReader(
            GetResponseStream(url, parameters)))
        {
            // Create an xml reader out of the stream reader.
            using (XmlReader xmlReader = XmlReader.Create(reader,
                new XmlReaderSettings()))
            {
                // Get a syndication feed out of the xml.
                return SyndicationFeed.Load(xmlReader);   
            }               
        }
    }

    /// <summary>
    /// Clean up the authentication token.
    /// </summary>
    public void Dispose()
    {
        auth = null;
    }
}

What you can do with it:

Putting it all together

Now that we can easily authenticate and make requests let’s make use of it. Here is an example of a command line application showing the last 5 articles from Google Reader.

class Program  
{
    static void Main(string[] args)
    {
        // Empty line.
        Console.WriteLine("");

        // Get username.
        Console.Write(" Enter your Google username: ");
        string username = Console.ReadLine();

        // Get password.
        Console.Write(" Enter your password: ");
        string password = Console.ReadLine();

        // Authenticate.
        string auth = ClientLogin.GetAuthToken("reader", username, password, "Sandworks.Google.App");

        // Query.
        using (GoogleSession session = new GoogleSession(auth))
        {
            var feed = session.GetFeed("http://www.google.com/reader/atom/user/-/state/com.google/reading-list",
                new GoogleParameter("n", "5"));

            // Display.
            Console.WriteLine("");
            Console.WriteLine(" Last 5 articles in Google Reader: ");
            foreach (var item in feed.Items)
            {
                Console.WriteLine("  - " + item.Title.Text);
            }
        }

        // Pause.
        Console.ReadLine();
    }
}

What it does:

And the result:

Don’t worry about the Google Reader url for now. In the next article I’ll talk about creating a class to talk to Google reader and how the URLs work.

Download: Sandworks.Google.zip (60.77 kb)

Enjoy..

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket
Comments powered by Disqus