1

I have a problem with my application. My goal is edit google spreadsheets from Java. But I have problem with authentication. I can authorize myself if I copy the auth code from browser, but is not a possible solution in my case. So first I'd like to write a siple program to authenticate myself, for example:

package com.googleoauth;


import java.io.IOException;

import java.util.ArrayList;

import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.google.common.collect.ImmutableMap;



public class GoogleOauthServer {

private Server server = new Server(8089);

private final String clientId = "141523981682-ee0an7hj3f82rphdud7940v9juniqkch.apps.googleusercontent.com";
private final String clientSecret = "_cCjAG9mlS9KufGzFan4Arni";
private final String callbackUri = "http://localhost:8089/callback";

public static void main(String[] args) throws Exception {
    new GoogleOauthServer().startJetty();
}

public void startJetty() throws Exception {

    ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
    context.setContextPath("/");
    server.setHandler(context);

    // map servlets to endpoints
    context.addServlet(new ServletHolder(new SigninServlet()),"/signin");        
    context.addServlet(new ServletHolder(new CallbackServlet()),"/callback");        

    server.start();
    server.join();
}

class SigninServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {

        // redirect to google for authorization
        StringBuilder oauthUrl = new StringBuilder().append("https://accounts.google.com/o/oauth2/auth")
        .append("?client_id=").append(clientId) // the client id from the api console registration
        .append("&response_type=code")
        .append("&scope=openid%20email") // scope is the api permissions we are requesting
        .append("&redirect_uri=").append(callbackUri) // the servlet that google redirects to after authorization
        .append("&state=this_can_be_anything_to_help_correlate_the_response%3Dlike_session_id")
        .append("&access_type=offline") // here we are asking to access to user's data while they are not signed in
        .append("&approval_prompt=force"); // this requires them to verify which account to use, if they are already signed in

        resp.sendRedirect(oauthUrl.toString());
    }   
}

class CallbackServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        // google redirects with
        //http://localhost:8089/callback?state=this_can_be_anything_to_help_correlate_the_response%3Dlike_session_id&code=4/ygE-kCdJ_pgwb1mKZq3uaTEWLUBd.slJWq1jM9mcUEnp6UAPFm0F2NQjrgwI&authuser=0&prompt=consent&session_state=a3d1eb134189705e9acf2f573325e6f30dd30ee4..d62c

        // if the user denied access, we get back an error, ex
        // error=access_denied&state=session%3Dpotatoes

        if (req.getParameter("error") != null) {
            resp.getWriter().println(req.getParameter("error"));
            return;
        }

        // google returns a code that can be exchanged for a access token
        String code = req.getParameter("code");

        // get the access token by post to Google
        String body = post("https://accounts.google.com/o/oauth2/token", ImmutableMap.<String,String>builder()
                .put("code", code)
                .put("client_id", clientId)
                .put("client_secret", clientSecret)
                .put("redirect_uri", callbackUri)
                .put("grant_type", "authorization_code").build());

        JSONObject jsonObject = null;

        // get the access token from json and request info from Google
        try {
            jsonObject = (JSONObject) new JSONParser().parse(body);
        } catch (ParseException e) {
            throw new RuntimeException("Unable to parse json " + body);
        }

        // google tokens expire after an hour, but since we requested offline access we can get a new token without user involvement via the refresh token
        String accessToken = (String) jsonObject.get("access_token");

        // you may want to store the access token in session
        req.getSession().setAttribute("access_token", accessToken);

        // get some info about the user with the access token
        String json = get(new StringBuilder("https://www.googleapis.com/oauth2/v1/userinfo?access_token=").append(accessToken).toString());

        // now we could store the email address in session

        // return the json of the user's basic info
        resp.getWriter().println(json);
    }   
}

// makes a GET request to url and returns body as a string
public String get(String url) throws ClientProtocolException, IOException {
    return execute(new HttpGet(url));
}

// makes a POST request to url with form parameters and returns body as a string
public String post(String url, Map<String,String> formParameters) throws ClientProtocolException, IOException { 
    HttpPost request = new HttpPost(url);

    List <NameValuePair> nvps = new ArrayList <NameValuePair>();

    for (String key : formParameters.keySet()) {
        nvps.add(new BasicNameValuePair(key, formParameters.get(key))); 
    }

    request.setEntity(new UrlEncodedFormEntity(nvps));

    return execute(request);
}

// makes request and checks response code for 200
private String execute(HttpRequestBase request) throws ClientProtocolException, IOException {
    HttpClient httpClient = new DefaultHttpClient();
    HttpResponse response = httpClient.execute(request);

    HttpEntity entity = response.getEntity();
    String body = EntityUtils.toString(entity);

    if (response.getStatusLine().getStatusCode() != 200) {
        throw new RuntimeException("Expected 200 but got " + response.getStatusLine().getStatusCode() + ", with body " + body);
    }

    return body;
}
}

When I tried this example I got an error: 401. That’s an error. Error: invalid_client The OAuth client was deleted.

I can't find any useful about this error.

If somebody can send an example, where I can authorize myself without any copy or other external intervention that would be very kind.

Thanks, Richard

1

1 Answer 1

1

I just got your example and started the application. Browsed to localhost:8089/signin then logged in with google, approved some permissions and finally I got the json response with my userinfo. Then restarted the application. The scenario still works. :)

The problem you encountered "could be" caused by some wrong dependencies you use in your code. My deps were following:

    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-server</artifactId>
        <version>9.3.0.M1</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-servlet</artifactId>
        <version>9.3.0.M1</version>
    </dependency>

    <dependency>
        <groupId>org.apache.clerezza.ext</groupId>
        <artifactId>org.json.simple</artifactId>
        <version>0.4</version>
    </dependency>

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava-collections</artifactId>
        <version>r03</version>
    </dependency>

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpcore</artifactId>
        <version>4.2.5</version>
    </dependency>

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.2.5</version>
    </dependency>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.