This month, I'm continuing last-month's discussion (Getting Started with Google Apps and OAuth) by providing both a working example and a discussion of the real-world issues that come up when you actually implement and test.
Getting Google OAuth to actually work is a frustrating process. The Google documentation is poorly organized and incoherent (in the sense of "scattered"), and a few pieces of critical information are missing altogether. The advice you find on the web is, for the most part, clueless guessing, and Google's code examples don't consider real-world issues like persistent user-specific token management. More to the point, the effort required just to set things up so that you can run and test the code is way more than the effort required to write the code.
I hope to fix those problems with this article.
The Network Environment
One of the hardest parts of writing OAuth code is arranging to test it. In particular, you must be able to receive the Auth token from Google. You'll remember from last month, that once your user has authorized access, Google will send an HTTP GET to an arbitrary URL that you supply. The access token is appended as a query parameter to the URL.
Testing this mechanism can be a challenge, given that most of our development machines are on private subnets (with 192.168.x.x or 10.x.x.x IP addresses), which are only accessible locally. Giving Google a private callback address is useless, since that address can't be accessed directly from the Internet.
You have a few choices, but first the non-choice: If you have a simple Internet connection accessed via a modem, where the upstream router (which could be your ISP, but could also be a corporate firewall) assigns a private 192.168.x.x or 10.x.x.x address to you, you're hosed. Google just can't send an HTTP request to a machine on a local network. Similarly, the odds of getting your IT department to allow Google to directly access your test machine are nil. There are workarounds, which I'll discuss in a moment, but you might have to work around your IT department, too. Testing can be a very political process.
Moving on to setups that work, let's start with the simple-to-the-point-of-useless-but-inexpensive approach, which works only if you're working at home or otherwise have control of the main Internet pipe. Sometimes, your ISP will assign a temporary, but public, IP addresss to you. That can work, provided that you can use that IP address directly, as will be the case if you're talking to the ISP through a simple modem. (If you have a router hooked up to the ISP through the modem, you can just unplug the router and plug your test machine directly into the ISP's modem, effectively co-opting the address that would normally be assigned to the router itself.) If your connection to the outside world is router rather than a modem, you'll have to know how to reprogram that router, so practically speaking, that's not going to work.
Note that if you do use a dynamic-ISP-provided IP address in this way, you'll have to manually change the "callback" URL that you send to Google to tell it where to send the Auth token (to reflect whatever your actual IP address happens to be at the moment). This is a source-code change, and it's annoying to do it every time you sit down to test, but as I said, this simple approach isn't really workable. The other disadvantage of this simple approach is that you're effectively disconnected from your LAN when you're plugged directly into the ISP's modem, so you can't print, talk to network disks, etc.
A more workable, but still not ideal, approach is to use an external server as an intermediary: Set up a public server that accepts Auth tokens and then passes them on to the real application. For example, you can create a Google-Apps application (which will have a public domain name that is a subdomain of appspot.com), and then deploy a servlet or equivalent to that domain to service the Google-generated HTTP requests that carry the Auth token. You give Google the URL of that intermediary-level server as its "callback" URL.
Your application (behind the firewall) fetches the token with its own HTTP request to the same intermediary, typically from a servlet with a different name than the one that Google's using. This second servlet responds to your request by returning the token (or tokens) that Google sent to it. Your main application will have to poll the servlet until the token arrives. (The whole point of this exercise is to make up for the fact that you can't push past the firewall, so polling is mandatory.)
This approach involves the minimum amount of messing around with the network at the cost of considerable programming and the annoyance of a dual-site deployment. You can, at least, deploy the intermediary servlets and the main application to the same machine in production, so the test code is the same as the production code. The URL has to change, but that's it. The obvious disadvantage is the not inconsiderable inconvenience, complexity, and time required to split your application into two parts and deploy them to separate locations.
The best way, if you can do it, is to actually set up a public IP address so that Google can talk directly to your development machine. This actual solution may or may not be possible if you work for a large company.
I (reluctantly, and with much gnashing of teeth, given the cost) pay my ISP for a pool of 5 static IP addresses. I've registered a domain name (timezer.com which is the application I'm current building) and attached one of those addresses to that domain.
Figure 1 shows the easiest setup that works. I've assigned one of the static IP addresses to the router that's supporting my normal wireless LAN, which my machine can uses to talk to the printer, etc. Since my machine has two network ports, I can also run a wire over to the modem, so I've manually assigned another static IP address to the machine's wired-ethernet port. My development machine, as a consequence, is effectively on both my LAN and the wild-wild Internet at the same time.
That simple solution works fine, with the one exception that there's no firewall between my development machine at the Internet. So, to get that firewall, I actually use the setup shown in Figure 2.
At the top left of Figure 2, the ISP's modem is connected to the Internet. That modem has a static IP address that serves as the "gateway address" for my local subnet.
I've connected the modem to a separate firewall/router that does the heavy lifting. I prefer this two-box approach to just using ISP-supplied equipment for two reasons: My box is more capable than the box that comes from my ISP (Comcast) my box supports a high-speed VPN and dual WAN ports, for example. Moreover, I can change ISPs without wasting a day figuring out how to set up the new ISP's equipment. On the other hand, the SMC cable-modem/router that Comcast supplied with the "business-class" service I use will happily do all the address mapping I'm about to describe. You don't have to go out and buy another expensive slab of hardware to make things work.
Moving on, the firewall supports two local networks. One is a standard LAN that parcels out addresses in the 192.168.1.x range via a wireless router. That is, the router maps its own IP address (70.x.x.2) to a pool of address in the 192.16.8.x range. Those addresses are assigned to machines on the LAN with DHCP, in the normal way.
The second local network is an independent "DMZ." that can't communicate directly with the LAN it's a separate subnet. The DMZ has a public IP address (70.x.x.3) that the router maps (using NAT) to an internal LAN address of 192.168.3.10. That is, the router passes all Internet-level requests to 70.x.x.3 to the local machine with the IP address 192.168.3.10. I allow only HTTP and ping requests to get through the firewall in this way.
Finally, we come to my development machine. The wireless port just connects to he LAN using DHCP. Now, however, the wired port has a static IP address that's inside the DMZ (192.168.3.10). As with the simpler setup, when I want to debug my app, I just plug in the wire, and the machine is now on both networks. But now, when Google issues an HTTP request to http://70.x.x.3 (or to the matching domain name), the firewall maps that address to the DMZ address (192.168.3.10). That mapping is transparent to Google. As far as it's concerned, my development box has the public IP address.
There are lots of variations to this theme. The main one would be to use a "virtual server." Here, the router uses NAT to map its own IP address to a specific local machine at a well-known LAN address. All requests to the router for connection using a specific protocol (like HTTP) are just forwarded to the specified machine on the LAN. There are no extra wires or multiple ports to mess with, since everything's on a single LAN. On the downside, the NAT mapping is typically done by IP address, so you have to configure your machine to use a static IP address (on the LAN) rather than DHCP, which is annoying if you want to work in a coffeehouse and have to use DHCP. If you have a router than can act as a virtual server using your machine's MAC address instead of its IP address, then the virtual-server approach is the easiest.