I'm working on an application in the browser that lets you take notes. I don't want to have the burden to save them on my own server therefore I want to use Github Gists as storage. The challenge is to be able to communicate with the Github API 100% inside the browser.

Since it is a difficult task due to Cross-origin resource sharing limitations and multi-step OAuth process, I decided to share with you a working procedure I found. It involves different communication protocols such as HTTP Redirect, window.postMessage, Ajax post and get and a small PHP proxy using cURL.

Login Phase

Phase 0 - Create an application

Before doing anything, you have to create a Github application. It will provide you the client_id and client_secret as well as an admin to put the redirect URL.

Phase 1 - Get authentication code

Using Github API OAuth guide we learn that we have to redirect the user to a page on github server. After the user authorizes the application, the page is redirected to one of our page with a code.

Since we do not want to leave the current page (it would make all the user changes vanish) we must open the page in another context. The first one I tried was an iframe but github has the X-Frame-Options header set that prevents embedding the page in the iframe.

So the other option was to open a new window. With window.open it was really easy to do so. The only tricky part was to actually give back the result to the main window. After digging, I found the following snippet of code that works well: window.opener.postMessage(message, window.location).

Phase 2 - Get access token

We are back in the main window and have the code. We now need to exchange this code for a token. I really wonder why they didn't give us the token already but well, there must be a reason! In order to get the token, we must send a POST request to a page on github.

However another difficulty comes in, this one page does not have a Access-Control-Allow-Origin header set to our domain. So basically, we cannot access it from the browser using AJAX. Since it's a POST request, we cannot even use JSON-P to bypass it.

I did not want to have a server but I am resigned to write a small PHP proxy that will forward the call. I believe that the main reason why they blocked it was because they ask for the client_secret. They don't want us to write it down in our Javascript in plain sight.

Phase 3 - Enjoy!

Now that we have got our token, we can call all the APIs on Github using post and get AJAX requests and they all work fine. One good thing is the fact that the token is permanent. Unless you change the permissions you request or the user revokes your application, every time the user logs in, he will be associated the same token.

You can safely store the token in the user's browser with localStorage in order to keep them logged when they come back to the application. Just make sure to catch 401 Unauthorized error on requests in case the token is no longer valid and ask the user to log in again.

Demo

And here's the demo! The source code is really small and available on github. If you plan to integrate an in-browser login, it can be used as a starting point.

You might want the link to revoke the access from the dummy application for testing purposes.

Conclusion

At first glance, the login process seemed to be really straightforward, you just had 2 requests to get your code and token and you are good to go. But doing so in the browser revealed itself to be a lot harder. I'm not satisfied with the process as it involves many different technologies but that's the best I could find. If you handled things differently please tell me ๐Ÿ™‚

If you liked this article, you might be interested in my Twitter feed as well.
 
  • http://jeditoolkit.com/ Irakli Gozalishvili

    Yeah OAuth is pain specially when you don't want to use server side code. Luckily github APIs can be used without oauth. Here is a small full client side app I wrote for displaying a gists: http://gistview.github.com/ and in order to get CORS working I had to register it as an app https://github.com/account/applications/

    For more details see the following thread:
    https://github.tenderapp.com/discussions/email/38918-contact-gist-api-missing-access-control-headers

    Also if you had user name / password you could use that instead of OAuth in combination with trick above to work with private APIs.

  • http://blog.vjeux.com/ Vjeux

    The public API can be used without OAuth but if you want to interact with user permissions, you sadly have to use it.
    Asking people for their user and password is a solution that would not involve having a server but it's something I would like to avoid at all costs ๐Ÿ™‚
    Thanks for the reply!

    --
    Christopher "vjeux" Chedeau
    Facebook Engineer
    http://blog.vjeux.com/

  • http://jeditoolkit.com/ Irakli Gozalishvili

    I hope https://browserid.org/ will take off and will make this less painful for backendless apps ๐Ÿ˜‰

  • anon

    How exactly can that be done? The thread with the details has apparently been deleted...

  • http://twitter.com/_mql Michael Aufreiter

    Here's a follow up: I've built a small configureable node application that does that thing described here.

    https://github.com/developmentseed/gatekeeper

    Thanks for the article!

  • vibhor mahajan
  • Andi

    thanks!

  • chaowang

    Must i follow the 3 steps in each sign in process? : redirect to github and request for code, get the code and post to github for token , and then ,we can use token to get user's information. I just think this process takes too many steps, works slowly. Can i store the access_token ? I have tested that the code from github can only be used once.

  • tristen

    Dead link - it's now here here: https://github.com/prose/gatekeeper

  • GuillaumeBalaine

    The trouble though, is that the api rates are not nearly the same when you're on Oauth and not.

  • GuillaumeBalaine

    The trouble obviously is that users are put off by having to sign in to yet another protocol.

  • Jaroslav Tulach

    This is a reply I've got today from github support:

    > As you've discovered, we don't support CORS [1] for a pure client-side flow
    > for getting an OAuth token. If you're wanting to let users authorize your
    > application, you'll need to support the web flow with your own server
    > backend [2].
    > [1]: http://developer.github.com/v3/#cross-origin-resource-sharing
    > [2]: http://developer.github.com/v3/oauth/#web-application-flow

    Thus I guess the solution outlined here is the best we can think of. Thanks for finding it out.

  • Ken Power

    thanks, that helped me out of a jam

  • testic

    It seems that this demo is not working...

  • http://thebacklog.net/ dirkcuys

    Thanks for sharing your experience. I was hoping to be able to do everything client side, maybe one day we'll get a client side compatible authentication flow for the web?

 

Related Posts

  • September 25, 2011 Javascript Object Difference (5)
    This article is about a difference algorithm. It extracts changes from one version of an object to another. It helps storing a smaller amount of information. Template In a project, I have a template object with all the default settings for a widget. var template = { […]
  • December 26, 2015 Challenge: Best JavaScript Setup for Quick Prototyping (25)
    Yesterday, there was a big discussion on Twitter on how hard it is to start hacking on a js project. One comment by Dan Abramov struck me in particular: "Right: donโ€™t use tools, face a problem, choose a tool or roll your own. Wrong: learn tools that donโ€™t solve your problems, hate the […]
  • October 30, 2009 Javascript – Dynamic Query Throttling (2)
    Working on the World of Raids Recruitment Tool we wanted automatic saving while editing. There are basically two ways of handling the problem. Send update everytime something changes. The main advantage of this technique is that you are always up to date. However, it is spamming the […]
  • August 20, 2011 Idea – mouseFreeze – A solution for Browser FPS Games (8)
    There is an open problem in porting real game into the web browser related to cursor handling. Problem Many games such as First-Person Shooters require the mouse to freely move, without the constraints of screen edges. However there is no such API in the browser to make this […]
  • February 18, 2013 XSON – Smallest JSON equivalent in XML (4)
    It's often said that XML is very verbose and therefore JSON is better. I wanted to challenge that assumption and find the smallest way to represent any JSON value using XML. table#xson { border-collapse: collapse; margin: 0 auto; } table#xson td, table#xson th { border: 1px solid […]