Setting up Google OAuth2 with Java
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
- 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
- Add Client ID, and Client Secret parameters to
GoogleAuthHelper.java
- Compile the project:
$ mvn clean install
- Deploy war to application server
- Browse to: http://localhost:8080/OAuth2v1/
- 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/
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.
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"))); } %>
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
Posted in Java, JBoss, OpenSource, Technology
Hi,
your article is very helpful, thank you!
Having a positive outlook on life I am now trying out to compile the current https://github.com/mdanter/OAuth2v1.git project!
I did not perform any source code change yet, but it fails with exception: Failed to resolve artifact
Hereafter part of the maven feedback (I remove some of it to make it more concise):
Missing:
———-
1) com.google.code.findbugs:jsr305:jar:${project.jsr305.version}
2) com.google.guava:guava-jdk5:jar:${project.guava.version}
3) org.apache.httpcomponents:httpclient:jar:${project.httpclient.version}
4) xpp3:xpp3:jar:${project.xpp3.version}
5) org.codehaus.jackson:jackson-core-asl:jar:${project.jackson-core-asl.version}
6) com.google.oauth-client:google-oauth-client:jar:${project.oauth.version}
6 required artifacts are missing.
for artifact:
com.danter.auth.google:OAuth2v1:war:0.0.1-SNAPSHOT
from the specified remote repositories:
google-api-services (http://google-api-client-libraries.appspot.com/mavenrepo),
jbossOrgRepo (https://repository.jboss.org/nexus/content/groups/public-jboss/),
central (http://repo1.maven.org/maven2)
I am using the following versions of Maven/Java:
Apache Maven 2.0.11 (r909250; 2010-02-12 06:55:50+0100)
Java version: 1.6.0_26
Just wondering if anything like Maven properties defining ${project.jsr305.version}, ${project.guava.version},… are missing or if I overlooked some information or prerequisites ?
Patrick
Patrick,
This is usually an indication that you don’t have access to the repositories that contain the libraries. Please take a look at this thread on stackoverflow: http://stackoverflow.com/questions/5409802/how-shoud-i-install-missing-artifacts-in-a-maven-project
Matyas,
thank you for your pointer to stackoverflow. I successfully "git clone" and "mvn clean install" your project in one go from another computer and network! The only other difference being that I use Maven 3.0.3 which should not matter.
From my other location I had to explicitly add some dependencies to have their Maven artifact "version" resolved! As follow:
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>1.3.9</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-jdk5</artifactId>
<version>13.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>xpp3</groupId>
<artifactId>xpp3</artifactId>
<version>1.1.4c</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.9</version>
</dependency>
<dependency>
<groupId>com.google.oauth-client</groupId>
<artifactId>google-oauth-client</artifactId>
<version>1.13.1-beta</version>
</dependency>
Finally following your instructions I had the "log in with google" work in less than 15min! Great job!
Thanks again,
Patrick
Thank you, Patrick! Thank you for providing this information for readers who may run into the same issue.
Thank you very much , I spent a long time trying to figure out oauth with java and this is very nicely written.
Hi Matyas Danter, Need your help….
First of all i would like to thank you for that helpfull code. I need to get the OAuth token information from that code behalf that google response after user authentication to an application.
When i get the user information by that code
how to setup the google accesstoken in my session, if session has stored user access token then set accessToken ( you may be fimiliar with that google function, i don’t know how to set that function in this example) and without showing Login with google button fetching user information or what we want.
For reference i need to know you Stackoverflow example. (when user loggin with google accounts stackoverflow keeps google user access long-lived even user logged out from google other websites.)
Another reference is "https://oauth2-login-demo.appspot.com/"
Thanks in advance
Hi Waqas Ahmed,
is the auth token that you may store in the session for repeated requests. This token does expire after a certain amount of time so you may not want to persist it or if you do you need to handle the Exception when the token expires and modify it. For more information, please take a look at the Basic Steps section under subsections 3. "Send Access Token to an API" and 4. "Refresh the Access Token (optional)" in the official documentation from google at https://developers.google.com/accounts/docs/OAuth2
if you are lazzy it is even works with Jetty on http://localhost:8080
and use
mvn jetty:run
I am getting – com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request { "error" : "invalid_client" } at flow.newTokenRequest(authCode).setRedirectUri(CALLBACK_URI).execute(),please help
It was due to CLIENT_SECRET mismatch .
#java
Hi
I am getting Connection refused: connect when calling the execute method of getUserInfoJson(). I am behind a proxy, is this causing problems?
Added the following JVM args to fix the problem.
http.proxyHost
http.proxyPort
https.proxyHost
https.proxyPort
Hello,
I managed to run example project using Eclipse and Struts2,
but now i am quite confused:
in getUserInfoJson we obtain access token,
what should i do next?
my problem is that i want to (for example)
display database content which is dependant to UserID,
how should i parse user info into another .jsp?
just make a string out of it?
Best regards
S
Hello, Need your help Please.. My application is prompting each time for having offline access. Could you please help me how to configure this.
Lets say i have implemented for an appln login. in that case asking for offline access each time does not make sense. Please let me know if i have to do something for this.
Thanking you in advance.
Could someone explain hoe the application getting started or where is the entry point ….
When we try to run this on JB 7, we’re getting: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
What certificate do we need to install in JBoss? One from Google? Confused on this part. I know how to install the certs, just not sure of the cert to install to get this running. π
I’m also getting this issue
have you fixed this issue, may I know how to fix this?
Thanks you very much! Worked like a charm
‘m trying to run this code but I’m getting SSLHandshakeException.
Once login success in google when I’m clicking on access button getting this below exception.
Can you please help me what certificate do I need to install
Exception –
Hi, Could you please help me how do i get inbox messages from gmail using access tocken….
please help me its very argent … plz plz…..
Hi
when i am running this project i am getting pls help.
401. Thatβs an error.
Error: invalid_client
The OAuth client was not found
Excelent example!!
It saved my day! Thanksss!!
This seems to be so much easier than using oAuth2 with Spring Security and worked straight with Eclipse Luna and Java 8 after a tweak for JSF.versions.
( http://stackoverflow.com/questions/8171862/problems-configuring-jsf-2-0-on-eclipse-indigo, https://bugs.eclipse.org/bugs/show_bug.cgi?id=201792 )
Thanks!
Hi ALL,
How to logout above code?
plz suggest me how to make logout option
hello, did you figure out the logout option?
I want to restrict the login to my application to a particular domain (eg:abc@university.edu). I read a few blogs that uses hd parameter. Could you tell me how to do it?Thanks
Hello,
i’m soooooooooooo happy for finding this help. i spent many days on search.
can anyone please give me a bit more information that which one is access token and which one is authorized code(from resource owner)?
I am trying to test an application that uses oauth2, not able to figure out how to get the access token that is used by my app