Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

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

4.55/5 (7 votes)
6 Jul 2010Ms-PL3 min read 32K   553  
What I would like to do in the following articles is connect to and consume Google services (with a focus on Google Reader) using .NET.

Introduction

Since a few weeks, 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 articles is 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 at http://code.google.com/apis/gdata/.

Downsides for me personally:

  • No support for the .NET Client Profile
  • Reference to external assembly required
  • No support/documentation for Google Reader?

It is also important to know 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 information can be found at http://groups.google.com/group/fougrapi/browse_thread/thread/e331f37f7f126c00

This solution:

  • Works with ClientLogin authentication
  • Using the .NET 4.0 Client Profile
  • GoogleSession class answering all your needs

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:

C#
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 known, 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:

  • Reusing the Auth token
  • Adding the Auth token to the request header (point 4 in the StackOverFlow post)
  • Support for parameters (like top 10 items, etc.)
  • Process the request in a reusable manner (Response as stream, string, feed, ...)

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

C#
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:

  • Create an authenticated response supporting parameters
  • Get this response as a Stream
  • Get this response as a feed
  • Get this response as a string (raw source)

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.

C#
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</a />",
                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:

  • Asks for your Google account
  • Asks for your password
  • Authenticates using ClientLogin and gets the Auth token
  • Creates a new GoogleSession using the Auth token
  • Connects to Google Reader and gets the last 5 articles (using a GoogleParameter).
  • Displays those articles

And the result:

GoogleReaderApp.png

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.

Enjoy...

History

  • 6th July, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)