1
votes

I'm trying to access an OAuth-protected resource on Google App Engine using a Java/Groovy client. However the authentication is not working and my GET requests are just bringing back the Google Accounts login page HTML.

I get the same results with HTTPBuilder/signpost and with google-oauth-java-client.

Here's what I've done:

  • Set up an OAuth provider as described in http://ikaisays.com/2011/05/26/setting-up-an-oauth-provider-on-google-app-engine/
  • Created a 'hello world' servlet (actually a Gaelyk groovlet) mapped to http://<my-app>.appspot.com/rest/hello
  • Deployed the servlet to gae and confirmed I can GET via a browser.
  • Added a security constraint to my web.xml and redeployed.

    <security-constraint>
          <web-resource-collection>
               <web-resource-name>Rest</web-resource-name>
               <url-pattern>/rest/*</url-pattern>
          </web-resource-collection>
          <auth-constraint>
               <role-name>*</role-name>
          </auth-constraint>
     </security-constraint>
    
  • Confirmed that a browser GET requires a Google Accounts login and that after login I can access the servlet.

  • Did the 3-legged OAuth dance as described in http://groovy.codehaus.org/modules/http-builder/doc/auth.html to get the access and client secret tokens.
  • Use the tokens in a RESTClient as follows (following instructions in the link above)

    def client = new RESTClient('http://<my-app>.appspot.com' )
    def consumerKey = <my consumer key>
    def consumerSecret = <my consumer secret>
    def accessToken = <my access token>
    def secretToken = <my secret token>
    client.auth.oauth consumerKey, consumerSecret, accessToken, secretToken
    def resp = client.get(path:'/rest/hello')
    assert resp.data == 'Hello world'
    
  • The assert fails since the response is the Google Accounts login page.

  • I get the same behaviour when using google-oauth-java-client.

I've been through the process above several times, checking for copy/paste errors in the tokens and ensuring that I'm not getting the tokens mixed up.

This is with Groovy 1.8.2, OSX Java 1.6.0_29, HTTPBuilder 0.5.1, gaelyk 1.1.

Any ideas? Thanks.

1

1 Answers

2
votes

OK, no response on this so here's how I worked around it.

I gave up on using oauth... google only claim 'experimental' status for this anyway so maybe it fundamentally doesn't work yet.

However I get good results using the ClientLogin protocol from my test client (equivalent to doing a manual login to Google Accounts like the one you do when accessing gmail)

I based this on the extremely useful article http://www.geekyblogger.com/2011/05/using-clientlogin-to-do-authentication.html. I had to extend in a few ways, code below:

import java.io.File;        
import java.io.InputStream;        
import java.io.LineNumberReader;        
import java.io.StringReader;        
import java.nio.charset.Charset;        

import org.apache.commons.io.IOUtils;        
import org.apache.http.Header;        
import org.apache.http.HttpResponse;        
import org.apache.http.client.HttpClient;        
import org.apache.http.client.methods.HttpGet;        
import org.apache.http.client.methods.HttpPost;        
import org.apache.http.entity.mime.MultipartEntity;        
import org.apache.http.entity.mime.content.StringBody;        
import org.apache.http.impl.client.DefaultHttpClient;        

import com.google.appengine.repackaged.com.google.common.io.Files;        
import com.google.cloud.sql.jdbc.internal.Charsets;        

public class Login {        

    public static void main(String[] args) throws Exception {        
        // This file contains my         
        // google password. Note that this has to be an app-specific         
        // password if you use 2-step verification        
        File passFile = new File("/Users/me/pass.txt");          
        String pass = Files.toString(passFile, Charsets.UTF_8);        
        String authCookie = loginToGoogle("[email protected]", pass,        
                "http://myapp.appspot.com");        
        DefaultHttpClient client = new DefaultHttpClient();        
        // A te        
        HttpGet get = new HttpGet("http://myapp.appspot.com/rest/blah");        
        get.setHeader("Cookie", authCookie);        
        HttpResponse response = client.execute(get);        
        response.getEntity().writeTo(System.out);        
    }        

    public static String loginToGoogle(String userid, String password,        
            String appUrl) throws Exception {        
        HttpClient client = new DefaultHttpClient();        
        HttpPost post = new HttpPost(        
                "https://www.google.com/accounts/ClientLogin");        

        MultipartEntity reqEntity = new MultipartEntity();        
        reqEntity.addPart("accountType", new StringBody("HOSTED_OR_GOOGLE",        
                "text/plain", Charset.forName("UTF-8")));        
        reqEntity.addPart("Email", new StringBody(userid));        
        reqEntity.addPart("Passwd", new StringBody(password));        
        reqEntity.addPart("service", new StringBody("ah"));        
        reqEntity.addPart("source", new StringBody(        
                "YourCompany-YourApp-YourVersion"));        
        post.setEntity(reqEntity);        
        HttpResponse response = client.execute(post);        
        if (response.getStatusLine().getStatusCode() == 200) {        
            InputStream input = response.getEntity().getContent();        
            String result = IOUtils.toString(input);        
            String authToken = getAuthToken(result);        
            post = new HttpPost(appUrl + "/_ah/login?auth=" + authToken);        
            response = client.execute(post);        
            Header[] cookies = response.getHeaders("SET-COOKIE");        
            for (Header cookie : cookies) {        
                if (cookie.getValue().startsWith("ACSID=")) {        
                    return cookie.getValue();        
                }        
            }        
            throw new Exception("ACSID cookie cannot be found");        
        } else        
            throw new Exception("Error obtaining ACSID");        
    }        

    private static String getAuthToken(String responseText) throws Exception {        
        LineNumberReader reader = new LineNumberReader(new StringReader(        
                responseText));        
        String line = reader.readLine();        
        while (line != null) {        
            line = line.trim();        
            if (line.startsWith("Auth=")) {        
                return line.substring(5);        
            }        
            line = reader.readLine();        
        }        
        throw new Exception("Could not find Auth token");        
    }        

}