Fun with WordPress HTTP API, Redirection and Cookies: WPGPlus 0.8.1
This weekend I checked in and released a new version (0.8, followed by 0.8.1 this am) of WPGPlus, the WordPress plugin I wrote which cross-posts to Google+ when new blog posts are published in WordPress.
Because Google hasn’t yet released a read-write API (their API only allows for reading data from Google+ not posting into it), the plugin uses a hack from this twitter bot script, and emulates the Google+ mobile interface: it logs in as you and posts on your behalf.
However, that script (and the earliest versions of WPGPlus) were using the PHP library cURL directly, and relied on an option in cURL called CURLOPT_FOLLOWLOCATION which enables cURL to follow 302 redirects. That’s fine, but this option is disallowed on many hosts running in safe mode – so for users on those hosts, that was the end of the line.
So, I decided to re-write the script using WordPress’ native HTTP API, which abstracts out the issue of what “transport” will be used, and offers nice safe sounding functions like wp_remote_request, wp_remote_post, and wp_remote_head. These functions essentially act as wrappers around the lower-level transports (cURL, fsockopen, and PHP streams) a given host might make available, taking a simple array of settings and returning arrays of results.
What I quickly learned was that impersonating a user for the purposes of logging into and posting to Google+ was harder than it looks. Each step required multiple redirects, and many of those redirects set cookies, which had to be stored and sent on subsequent requests. Each redirect had to be handled specifically in order to avoid the cURL safemode error. (Keeping with the theme of using native WordPress constructs, I created functions to store the cookies and retrieve them using the Transients API – which can usefully store WordPress_Http_Cookie objects – thanks to Code Compost for info on those objects).
Here’s the basic flow:
- WPGPlus requests “https://plus.google.com/” using a mobile user agent. Gets redirected (302 redirect) to a login form. Cookies get set for GAPS and GALX at this point.
- WPGPlus POSTs to “https://accounts.google.com/ServiceLoginAuth” with the username and password, and the GAPS and GALX cookies. Gets redirected to “https://accounts.google.com/CheckCookie” (with a bunch of stuff in the querystring). Cookies set here for NID, SID, LSID, HSID, SSID, APISID, SAPISID.
- WPGPlus requests “https://accounts.google.com/CheckCookie”, gets redirected to “https://plus.google.com/app/plus/x/?login” with updated cookies for GAPS, SID, LSID
- WPGPlus requests “https://plus.google.com/app/plus/x/?login”, gets redirected to “https://plus.google.com/app/plus/x/?login=1″ sets cookies for MEX (expired), updated cookie for SID
- WPGPlus requests “https://plus.google.com/app/plus/x/?login=1″, gets redirected to “https://plus.google.com/app/plus/x/sdfsdf/?login=1″, (the part between /x/ and /?login is a unique string) sets MEX cookie again (still expired), updates SID
- WPGPlus requests “https://plus.google.com/app/plus/x/asdfasdf/?login=1″, gets redirected to “https://plus.google.com/app/plus/x/?v=stream”, sets MEX again, updates cookie for SID,
- WPGplus requests “https://plus.google.com/app/plus/x/?v=stream”, gets redirected to “https://plus.google.com/app/plus/x/code/?v=stream”, sets MEX again, updates SID
- Then, at the point where we want to post an update, WPGPlus requests “https://m.google.com/app/plus/x/?v=compose&group=b0&hideloc=1″, gets redirected to “https://m.google.com/app/plus/x/1r2q2131bmi4/?v=compose&surl=%3Fv%3Dstream&sspath=%2Fapp%2Fplus%2Fx&group=b0&hideloc=1″ where the part between /x/ and /?v=compose is unique.
The question I’m now trying to figure out is, are all these redirects and cookies necessary for the plugin to operate effectively?
Many stages along the way set a cookie named “MEX” which is set to expired since 1990. It looks like the SID (session ID perhaps?) is the one getting updated all the time – is that the only one necessary to keep session going?
These are session cookies (expire at the end of the session): GALX, SID, LSID, HSID, SSID, APISID, SAPISID
This one has an expiration dates long in the future: GAPS (looks like 2 years), WML (10 years?).
The NID cookie seems to have an expiration date 6 months in the future, whereas the MEX cookie has one in the past: 01-Jan-1990 00:00:00 GMT.
I will need to keep experimenting, eliminating some of the cookies and maybe even just ignoring some of the redirects – maybe I only need to get past the “CheckCookie” step, and maybe we only need SID and GAPS cookies for these purposes. Would certainly make the plugin easier to maintain if I could take a few steps out of the flow.