Duke oAuth

A guide to adding Google and GitHub OAuth sign-in to your Java application

One of the most recent features that we’ve added to Takipi is 3rd party sign-in. If you’re remotely lazy like I do, then I guess you also prefer skipping on filling up forms and making up new passwords. When available, many people prefer a 3rd party sign-in as long as the permissions they’re asked for are non-intrusive – no-one really wants random posts showing up on their Facebook wall, so this kind of access includes only the user’s basic info like name and email addresses. In this post you’ll get a sneak peak to how we implemented this in Takipi, how you can use 3rd party sign-in in your application and a few insights that we came across that will save you some precious time if you decide to integrate this with your own application.

In our own little universe, it turned out I’m not alone in this and many of our users feel the same way. Since we’re a developer tools startup, using GitHub’s and Google’s 3rd party sign-in made perfect sense. Beyond the convenience aspects for your users, using a 3rd party service can enhance their security. Since Google and GitHub use Two-factor authentication (2FA) your application gets to enjoy this level of security for “free”. In our case we already had 2FA enabled so I guess this makes us… 3FA 😛

New Takipi Sign-in
Sneak preview of the mockup for our new homepage with the 3rd party sign-in buttons

To see how this works in practice, you can check out this page we used to test it internally. It totally works but the update is not live on the site yet. You have a rare opportunity here to be one of the first to use it 🙂

So remind me how OAuth 2.0 goes again?

The OAuth sign-in flow goes like this: A user goes to your site, clicks on the “Sign-in with Whatever” button and gets redirected to a permissions page. The permissions page is coming from Whatever™ and when they approves the permissions you’re asking for, Whatever™ sends them a token which his browser then sends to your application’s backend. Once you’re in possession of the token, you send it back to Whatever™ for validation and assuming it’s verified – you get access to the data you were granted permissions for.

The Google OAuth2.0 library

For implementing 3rd party sign-in on our site, we used Google’s APIs Client Library for Java. Which also has Jackson2, ProtoBuf and all kinds of other handy utilities that appear on the top 100 libraries GitHub’s top Java projects use. This was a pretty straightforward choice for us since we already use this library for other purposes, and, well, honestly, the Google library just came up as the first result on… Google. Prepare your tinfoil hats.

Find the Crap in Your Java App

Show me how >>

Fred

Implementing sign-in with Google

This was the easy part of the journey; the documentation on Google’s end was clear and to the point. Since it’s their own library they also abstract parts of the procedure and handle it behind the scenes, making it even easier to implement. The first step would be to create a project on Google’s developer console where you can register your application and customize the permissions page.

Google Permissions
Google’s permissions page – Customized for Takipi

Now back to Java. At the heart of it, the procedure boils down to a few simple steps with no surprises. First we build and execute a request to get a GoogleTokenResponse so we can verify the token we got from the user. Then we use this response to create a GoogleCredential that lets us invoke getAccessToken() on it and return the user’s info in a JSON format:


JsonFactory jsonFactory = new JacksonFactory();
HttpTransport httpTransport = new NetHttpTransport();

GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(
    httpTransport, jsonFactory,
    /* Client ID and Secret */
    ,
    code, "postmessage").execute();

GoogleCredential credential = new GoogleCredential.Builder()
    .setJsonFactory(jsonFactory)
    .setTransport(httpTransport)
    .setClientSecrets( /* Client ID and Secret */ ).build()
    .setFromTokenResponse(tokenResponse);

Oauth2 oauth2 = new Oauth2.Builder(httpTransport, jsonFactory, credential).setApplicationName("YourAppName").build();
Tokeninfo tokenInfo = oauth2.tokeninfo().setAccessToken(credential.getAccessToken()).execute();

return oauth2.userinfo().get().execute();

Boom. That’s it. The best advice here would be to just follow the official documentation. On GitHub’s end, the implementation gets a bit trickier.

Implementing sign-in with GitHub

Now we’re hit with the realization that Google helped us cut some corners with regard to OAuth and use an abstraction with GoogleTokenResponse and GoogleCredential. With GitHub we’ll have to be closer to the raw protocol. Similarly to Google, we can customize the permissions screen when registering our application on GitHub’s developer applications screen.

GitHub’s permissions page – Customized for Takipi

When we got to the actual code, there were 3 main issues that slowed us down a bit and we couldn’t find in the documentation. Here’s how we solved them:

1. Building your own request flow

What was only 5 lines of code in Google’s case, turns to 19 lines so we can generate a GitHub tokenResponse. Here’s how to build the request by yourself:

JsonFactory jsonFactory = new JacksonFactory();
HttpTransport httpTransport = new NetHttpTransport();

AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(
    BearerToken.authorizationHeaderAccessMethod(),
    httpTransport, jsonFactory,
    new GenericUrl("https://github.com/login/oauth/access_token"),
    new ClientParametersAuthentication( /* Client ID and Secret */ ),
    /* Client ID */
    "https://github.com/login/oauth/authorize").build();

TokenResponse tokenResponse = flow
    .newTokenRequest(code)
    .setScopes(Collections.singletonList("user:email"))
    .setRequestInitializer(new HttpRequestInitializer() {
        @Override
        public void initialize(HttpRequest request) throws IOException {
            request.getHeaders().setAccept("application/json");
        }
    }).execute();

2. Expecting a JSON response? For some reason String is the default

Noticed that initialize method earlier that does setAccept? This was a bit surprising. Turns out you need to explicitly ask GitHub to send you the response back in JSON or else you’ll get it in a string format. If we would’ve known that in advance it could have saved us some time looking at it like:

Mcgyver
But thankfully we found some duct tape and a paperclip to get a handle on things

3. Handling user emails for GitHub sign-in

Now, a GitHub user may have more than one email address and then you’ll have to choose which one you’ll use for your own purposes. For this reason we created a getBestEmail method that sorts out the email array (notice that it should extend ArrayList to work). After sorting the email array, we pick the best option we could find:

public class GithubEmails extends ArrayList & lt;
GithubEmail & gt; {
    public GithubEmail getBestEmail() {
        if (isEmpty()) {
            return null;
        }

        Collections.sort(this, GithubEmail.bestEmailComparator);

        return get(0);
    }
}

So what’s the best option? Each field in this email array we get in the response from GitHub has an email, a verified field and a primary field. Here’s the sorting comparator we implemented for sorting it:


public static final Comparator & lt; ? super GithubEmail & gt;
bestEmailComparator =
    new Comparator & lt;
GithubEmail & gt;
() {
    @Override
    public int compare(GithubEmail o1, GithubEmail o2) {
        if (o1.verified != o2.verified) {
            return (o1.verified ? -1 : 1);
        }

        if (o1.primary != o2.primary) {
            return (o1.primary ? -1 : 1);
        }

        return o1.email.compareTo(o2.email);
    }
};

So basically you can see that we prefer verified and primary emails.

Conclusion

We hope this post helped you wrap your head around how to approach OAuth and Java through Google’s and GitHub’s sign-in. While this is the way we decided to implement it, there are other ways and libraries you can use to achieve similar results. We’d be happy to hear which approach you took, which libraries you prefer and answer any questions about this implementation. If you have any comments or suggestions on the way we decided to do it, please let me know in the comments section below. And if you find yourself running into trouble, please share where you’re stuck and we’ll be happy to help and share more of the code.

15 Tools to help you gain better visibilty into your environment – View tool list

email
Some kind of monster @ OverOps, GDG Haifa lead.
  • peavers

    Fantastic post, quick question though; following with your example could you point out/explain how/where to get the code variable from on line 13 of the “Building your own request flow” code block?

    • Odonya Japheth

      Hi peavers, I was following this tutorial but can’t quite figure out where that code variable on line 13 (.newTokenRequest(code)) is coming from. Did someone clarify for you?

      • http://www.takipi.com/ Alex Zhitnitsky

        Hey, just answered the question from the other comment. Sorry for the late reply.

    • http://www.takipi.com/ Alex Zhitnitsky

      Hi Peavers,

      Sorry for the later reply! The variable ‘code’ is part of the response GitHub gives us after the user authenticates with them. See further explanation here – https://developer.github.com/v3/oauth/#web-application-flow
      More specific – https://developer.github.com/v3/oauth/#2-github-redirects-back-to-your-site

  • Maria

    Hi! Thank you for the tutorial!
    I have the same question but refering to Google’s one. There is a variable “code” what is it?
    And when it says/* Client ID and Secret */ how can you put them together?

  • Jonathan Camilo

    Hi! Great tutorial!
    What jar contain OAuth2 class?

  • http://www.developernepal.com Taj Bist

    Great tutorial…!

  • Ankur Gupta

    Hi Alex,

    Could you help me integrating the Git Hub login functionality for my web application. I am new to web development. I am using angular js , java and java script for the web development.

    Thank you very much in advance.
    Ankur