Using AuthSub with the .NET Client Library

Jeff Fisher, Google Data APIs team
August 2007

Introduction: Why is AuthSub Important?

The great thing about the Google Data APIs ("GData" for short) is how they allow developers to build applications that interact with Google services. More specifically, they allow you to access private user data for use in your application. The APIs enable you to write applications to synchronize, import, export, and otherwise manage that data. While the APIs grant you these powerful abilities, you have to remember to use them responsibly. Since user data is private information, naturally you want to access it in a secure manner. A key part of this is being able to authenticate to Google's servers in a secure fashion.

Let's say you have a great new web application that you want to have tied into the data stored in Google web services. Now you want to authenticate in order to access this private data. Why not just use something simple, like ClientLogin? Well, that will do the trick, but then you are handling even more private data: the user's login credentials. ClientLogin requires your application to ask for the user's Google username and password. This is okay for a desktop application that is running on the user's personal machine, but is less than ideal for a web-based application. Besides the liability of handling these credentials on your own server, perhaps some of your more cautious users would be afraid that you might store their information. Another common concern from users is if they only wish to grant a program access to one particular service (like the events on their Google Calendar) but not to some other service (like their Google Documents). AuthSub solves both of these problems by letting a user authenticate through Google's servers and allows your program to only request the access that it needs.

Now that you've read enough about the theory behind AuthSub, it's time to move on to some coding! For this article, I chose to keep things simple and do everything inside of a single ASP page, but you should be able to easily integrate the techniques demonstrated here into your own application.

Handling Authentication

So what is needed to actually use AuthSub in your web application? First, there are some standard imports from the GData client library:

<%@ Import Namespace="Google.GData.Client" %>
<%@ Import Namespace="Google.GData.Extensions" %>
<%@ Import Namespace="System.Net" %>

Now the first thing you have to do is send the user to a specially-crafted URL. This is what allows Google's servers to handle the authentication and then redirect the user back to your website. Fortunately, you don't have to generate this URL manually, as there are methods to do so for you. Let's look at an example:

authSubUrl = AuthSubUtil.getRequestUrl(target, scope, secure, session);
  • target This is a string containing the URL to your web application. This is where the user will be redirected to after authenticating.
  • scope This string is determined by what API you are using. It corresponds to one of the feeds in a GData API. For example, the feed containing all the calendar information for a user is "http://www.google.com/calendar/feeds/default/private/full".
  • secure This is a boolean that tells the server you have registered with Google and will cryptographically sign your requests to the server. This argument is usually false by default, especially when working in a test environment.
  • session This is another boolean that indicates that you would like a "session token" rather than a "one-time use token". The role of this argument will become more clear in a moment.

After the user clicks in the generated URL, they will be taken to a Google Accounts page which allows them to sign in to their Google account. They will then be redirected back to the web page you specified in the "target" variable, but with a query parameter "token" which contains a one-time use token. Normally, this token can be used exactly once. That is to say, it can be used to perform one action on a given feed. However, if you specified the "session" parameter as true, it can be exchanged for a "session token" that can be reused until the user ends the session. You can do this in the following manner:

String token = Request.QueryString["token"];
Session["token"] = AuthSubUtil.exchangeForSessionToken(token, null).ToString();

Here you extract the token from the query parameter and exchange it for a "session token". Then, so that it could be saved for later use, you can chose to store it in the automatic Session array of .NET. Naturally, you could also choose to store the token within a database. The next step is to use this token to make an authenticated request:

GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "My-Cool-Application");
authFactory.Token = (String) Session["token"];
CalendarService service = new CalendarService(authFactory.ApplicationName);
service.RequestFactory = authFactory;

Here you set up a CalendarService object to interact with the Google Calendar API using AuthSub for authentication. Note the "cl" used in the constructor for GAuthSubRequestFactory is the service name for Calendar. You can consult the Google Data APIs FAQ for other service names.

Secure (Registered) AuthSub

If you choose to register your web application, then you can enable an additional level of security while using AuthSub. This allows you to digitally sign all requests made by your code, making it so someone can't use AuthSub tokens issued to you unless they have your private key. The first step is making sure you are generating the correct AuthSub link when calling AuthSubUtil.getRequestUrl by setting the "secure" argument to true. There are two other code changes you will need to make:

String token = Request.QueryString["token"];
Session["token"] = AuthSubUtil.exchangeForSessionToken(token, rsaKey).ToString();

...

authFactory.PrivateKey = rsaKey;

First, notice instead of null, you now pass the variable "rsaKey" to the exchangeForSessionToken method. This same variable is also used to set a property of our GAuthSubRequestFactory when setting up the connection to the service. The "rsaKey" variable is an RSACryptoServiceProvider corresponding to the private key component of the x509 certificate that you registered with Google.

Generating an RSA private key and self-signed certificate can be a little confusing, especially since the .NET framework doesn't understand keys or certificates stored in the PEM format. The following commands show how to generate a private key and public certificate using the OpenSSL suite of tools:

openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj \
  '/C=US/ST=CA/L=Mountain View/CN=www.example.com' -keyout \
  test_key.pem -out test_cert.pem

openssl pkcs12 -export -in test_cert.pem -inkey test_key.pem \
  -out test_cert.pfx -name "Testing Certificate"

The first step generates a private key and a public X509 certificate both in PEM format called "test_key.pem" and "test_cert.pem", respectively. Note the certificate is set to be registered to "www.example.com" based in Mountain View, CA, US. Substitute the proper values for your company here. The "test_cert.pem" file contains the information you need to submit on the AuthSub registration page.

The second step generates a PFX file from your private key and certificate. This file can be imported into the .NET client library to digitally sign requests made to the GData APIs. The following code shows how you can import the private key from the PFX file into a web application:

protected AsymmetricAlgorithm getRsaKey()
{

  X509Certificate2 cert = new X509Certificate2("C:/MyAspSite/test_cert.pfx","");
  RSACryptoServiceProvider privateKey = cert.PrivateKey as RSACryptoServiceProvider;

  return privateKey;
}

The getRsaKey() function defined by this snippet can be used in place of the "rsaKey" variable shown above when used to authenticate to the APIs. Naturally, the file path should be replaced with the appropriate location of the PFX file you generated.

Complete Code Listing

The easiest way to show how to use the methods demonstrated in the previous section is with a live example. The following sample code is a simple ASP page that uses AuthSub to authenticate the user, then prints out the events of their Google Calendar.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<%@ Import Namespace="Google.GData.Client" %>
<%@ Import Namespace="Google.GData.Extensions" %>
<%@ Import Namespace="Google.GData.Calendar" %>
<%@ Import Namespace="System.Net" %>

<script runat="server">
    void PrintCalendar() {

        GAuthSubRequestFactory authFactory = new GAuthSubRequestFactory("cl", "TesterApp");
        authFactory.Token = (String) Session["token"];
        CalendarService service = new CalendarService(authFactory.ApplicationName);
        service.RequestFactory = authFactory;

        EventQuery query = new EventQuery();

        query.Uri = new Uri("http://www.google.com/calendar/feeds/default/private/full");

        try
        {
            EventFeed calFeed = service.Query(query);
            foreach (Google.GData.Calendar.EventEntry entry in calFeed.Entries)
            {
                Response.Write("Event: " + entry.Title.Text + "<br/>");
            }
        }
        catch (GDataRequestException gdre)
        {
            HttpWebResponse response = (HttpWebResponse)gdre.Response;
            
            //bad auth token, clear session and refresh the page
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                Session.Clear();
                Response.Redirect(Request.Url.AbsolutePath, true);
            }
            else
            {
                Response.Write("Error processing request: " + gdre.ToString());
            }
        }
    }

</script>


<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Test Site</title>
</head>
<body>

    <form id="form1" runat="server">
    <h1>AuthSub Sample Page</h1>
    <div>
    <%
        GotoAuthSubLink.Visible = false;
        
        if (Session["token"] != null)
        {
            PrintCalendar();
        }
        else if (Request.QueryString["token"] != null)
        {
            String token = Request.QueryString["token"];
            Session["token"] = AuthSubUtil.exchangeForSessionToken(token, null).ToString();
            Response.Redirect(Request.Url.AbsolutePath, true);
        }
        else //no auth data, print link
        {
            GotoAuthSubLink.Text = "Login to your Google Account";
            GotoAuthSubLink.Visible = true;
            GotoAuthSubLink.NavigateUrl = AuthSubUtil.getRequestUrl(Request.Url.ToString(),
                "http://www.google.com/calendar/feeds/",false,true);
        }
        
     %>
    <asp:HyperLink ID="GotoAuthSubLink" runat="server"/>

    </div>
    </form>
</body>
</html>

Conclusion

AuthSub allows your web application to access data stored in a user's Google account in a secure and controlled manner. Using the .NET client library makes it easy to integrate your ASP-based website with Google services. This article is meant to get you started, but there are additional resources that you are encouraged to consult: