Red Hat
Feb 25, 2013
by Matyas Danter
oauth_logo

For all of you who are trying to figure out how to integrate with Google’s single sign-on functionality, this article might be for you. I’ve taken the liberty of condensing all of the actual logic required to perform OAuth Google login, and provided it as a class and a JSP (seen below). In order to follow along better, I suggest cloning the example GitHub repository , and deploying to the application to your server of choice .

Assumptions

  • Familiarity with object oriented programming, Java, Maven, and Java EE
  • An IDE, it helps if you are comfortable using one (i.e. Eclipse)
  • Java application server listening on localhost:8080

Prerequisites

1google_oauth_access
  • Google API Access credentials (Client ID, Client Secret). Set it up here https://code.google.com/apis/console/
  • Set up allowed Redirect URIs at Google API → API Access . Input: http://localhost:8080/OAuth2v1/index.jsp
  • The source code referenced in this article from GitHub.
  • A positive outlook on life.

Please use the link above to set up API Access Credentials. When you are finished, you will see a similar page to the one below. Use your Client ID/Secret from this page to replace the values of the String constants in GoogleAuthHelper.java .

Usage

  1. Add Client ID, and Client Secret parameters to GoogleAuthHelper.java
  2. Compile the project:
    $ mvn clean install
  3. Deploy war to application server
  4. Browse to: http://localhost:8080/OAuth2v1/
  5. Click "log in with google" on top of the page

1. Add Client ID and Secret

Replace the constants following constant values in GoogleAuthHelper.java with the values provided to you by Google API Access.

/**
 * Please provide a value for the CLIENT_ID constant before proceeding, set this up at https://code.google.com/apis/console/
 */

private
 static
 final
 String
 CLIENT_ID =
 "YOUR ID HERE"
;

 
/**
 * Please provide a value for the CLIENT_SECRET constant before proceeding, set this up at https://code.google.com/apis/console/
 */

private
 static
 final
 String
 CLIENT_SECRET =
 "SUPER SECRET SAUCE"
;

2, 3, & 4. Compile the Project, Deploy the WAR, Open that Browser

This is a Maven based project, so issue a Maven install command to build the project and assemble the war file from the root of the project (where the pom.xml file is located).

$ mvn clean install

When Maven is finished creating the web archive, deploy it to your favorite server and navigate to http://localhost:8080/OAuth2v1/

2click_auth

5. Click "log in with google"

Now that your app server is running, the application is deployed, and your web browser is pointed at the application’s context root, you will see a page similar to the one below. I double dog dare you to click that log in button. You know you want to.

3result

After successful authentication you will see the page below, but there are a few important things to notice:

  1. The URL changed, now it contains two request parameters, state , and code .
  2. The page contains JSON output of your google account’s profile information.

Source Code

The authentication is possible thanks to the GoogleAuthorizationCodeFlow class. This class uses the Builder pattern to provide most of its functionality. GoogleAuthHelper’s no-argument constructor initializes the Flow using your client ID, secret, and other constants. The buildLoginUrl() method constructs the Google authentication URL based on the CALLBACK_URI and returns it as a Java String. This CALLBACK_URI must match one of the redirect URIs that you set up at Google’s API Access page. Upon successful authentication, OAuth2 will redirect you to CALLBACK_URI , and append the state and code GET request parameters to it. Please note, that the state request parameter has two purposes, one is to help differentiate authentication providers (i.e. Facebook OAuth, Google OAuth, or your own custom OAuth provider), the other and more important purpose is to pass an anti-forgery state token.

We need to use the code GET request parameter as the input for the getUserInfoJson(String authCode) method. If all is well, this method will return a JSON encoded Google profile as a Java String.

Here is the basic code that you can snip into your project:

<%
/*
 * The GoogleAuthHelper handles all the heavy lifting, and contains all "secrets"
 * required for constructing a google login url.
 */
final GoogleAuthHelper helper = new GoogleAuthHelper();
if (request.getParameter("code") == null
 || request.getParameter("state") == null) {
 /*
  * initial visit to the page
  */
 out.println("<a href='" + helper.buildLoginUrl() + "'>log in with google</a>");

 /*
  * set the secure state token in session to be able to track what we sent to google
  */
 session.setAttribute("state", helper.getStateToken());


} else if (request.getParameter("code") != null && request.getParameter("state") != null && request.getParameter("state").equals(session.getAttribute("state"))) {

 session.removeAttribute("state");

 /*
  * Executes after google redirects to the callback url.
  * Please note that the state request parameter is for convenience to differentiate
  * between authentication methods (ex. facebook oauth, google oauth, twitter, in-house).
  * 
  * GoogleAuthHelper()#getUserInfoJson(String) method returns a String containing
  * the json representation of the authenticated user's information. 
  * At this point you should parse and persist the info.
  */

 out.println(helper.getUserInfoJson(request.getParameter("code")));
}
%>
GoogleAuthHelper.javaView Complete File
public final class GoogleAuthHelper {

 /**
  * Please provide a value for the CLIENT_ID constant before proceeding, set this up at https://code.google.com/apis/console/
  */
 private static final String CLIENT_ID = "YOUR ID HERE";
 /**
  * Please provide a value for the CLIENT_SECRET constant before proceeding, set this up at https://code.google.com/apis/console/
  */
 private static final String CLIENT_SECRET = "SUPER SECRET SAUCE";

 /**
  * Callback URI that google will redirect to after successful authentication
  */
 private static final String CALLBACK_URI = "http://localhost:8080/OAuth2v1/index.jsp";
 
 // start google authentication constants
 private static final Iterable<String> SCOPE = Arrays.asList("https://www.googleapis.com/auth/userinfo.profile;https://www.googleapis.com/auth/userinfo.email".split(";"));
 private static final String USER_INFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo";
 private static final JsonFactory JSON_FACTORY = new JacksonFactory();
 private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
 // end google authentication constants

 private String stateToken;
 
 private final GoogleAuthorizationCodeFlow flow;
 
 /**
  * Constructor initializes the Google Authorization Code Flow with CLIENT ID, SECRET, and SCOPE 
  */
 public GoogleAuthHelper() {
  flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
    JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, SCOPE).build();
  generateStateToken();
 }

 /**
  * Builds a login URL based on client ID, secret, callback URI, and scope 
  */
 public String buildLoginUrl() {
  
  final GoogleAuthorizationCodeRequestUrl url = flow.newAuthorizationUrl();
  
  return url.setRedirectUri(CALLBACK_URI).setState(stateToken).build();
 }
 
 /**
  * Generates a secure state token 
  */
 private void generateStateToken(){

  SecureRandom sr1 = new SecureRandom();

  stateToken = "google;"+sr1.nextInt();

 }

 /**
  * Accessor for state token
  */
 public String getStateToken(){
  return stateToken;
 }

 /**
  * Expects an Authentication Code, and makes an authenticated request for the user's profile information
  * @return JSON formatted user profile information
  * @param authCode authentication code provided by google
  */
 public String getUserInfoJson(final String authCode) throws IOException {

  final GoogleTokenResponse response = flow.newTokenRequest(authCode).setRedirectUri(CALLBACK_URI).execute();
  final Credential credential = flow.createAndStoreCredential(response, null);
  final HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(credential);
  // Make an authenticated request
  final GenericUrl url = new GenericUrl(USER_INFO_URL);
  final HttpRequest request = requestFactory.buildGetRequest(url);
  request.getHeaders().setContentType("application/json");
  final String jsonIdentity = request.execute().parseAsString();

  return jsonIdentity;

 }
}

Next Steps

If you haven’t already downloaded the code and run it, I suggest you do that before bringing it into your codebase. There are a few Maven dependencies in the POM that you will need to include.

From here on, you may parse this information using Jackson , and persist it to a database or other data store. These are the basic building blocks with which you should be able to “get the stuff done.”

Resources and Links

About the author:

Matyas Danter is a Senior Associate at LiquidHub currently working at a large financial institution as a Production Support Specialist. He is interested in cryptography, software development awesomeness, and enterprise web applications.