Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at BFusion / BFLEX 2009 (Bloomington, Indiana) with:

Getting GitHub's v3 API To Work With oAuth

By Ben Nadel on
Tags: ColdFusion

For a few months, I've been using GitHub's Gist functionality as a way to color-code my blog entries. I host the vanilla code in the content of my blog; but, then I replace it (after the page has loaded) with the color-coded Gist version. This was working fairly well until about two weeks ago when I suddenly started getting "peer not authenticated" errors on my CFHTTP requests to the GitHub API.

Seeing as I hadn't changed anything on my end, I assumed the problem was on the GitHub side; so, I went to look at their API docs. From what I can tell, it looks like the version of the API that I was using (v2?) no longer supports Gist creation. Furthermore, v3 - the current version - requires oAuth-based authorization for Gist creation. I had been using Basic Authentication.

But, the "peer not authenticated" problem is not an API problem - it's a certificate problem. It looks like GitHub is now using a new SSL certificate for their API that doesn't come in-built with ColdFusion 8 (or 9). So, I went to Steven Erat's timeless blogpost on using Java's Keytool to import new SSL certificates.

At first, I kept getting the wrong SSL certificate (the one at developer.github.com); but, once I was able to get the api.github.com SSL certificate, importing it into my cacerts file fixed my "peer not authenticated" problem.

Once that was done, I had to figure out how to work within the v3 oAuth workflow. The code for my blog is pretty ghetto. I'd love to make it awesome - but, the fact is, it works and I don't have enough hours in the day! So, I settled on a rather awkward approach:

  1. Obtain an authorization (ie. oAuth token).
  2. Post Gist.
  3. Delete the authorization.

The reason that I am creating and then immediately deleting my authorization record is that I don't have a way to cleanly cache it in my application (remember, I'm doing this quick and dirty). As such, I need to delete the authorization record in order to prevent dozens of them from showing up in my GitHub account.

Here is the test code that I came up with:

  • <!--- Define the base URL of the v3 GitHub API. --->
  • <cfset baseAPI = "https://api.github.com/" />
  •  
  • <!---
  • Set up empty authorization values. These are the values that
  • we will have to clean up at the end - we'll be creating, using,
  • and then destroying an authorization token.
  •  
  • NOTE: This isn't the "right" way to do it - I simply don't have
  • an architecture that can store authorization tokens.
  • --->
  • <cfset authorizationID = "" />
  • <cfset authorizationToken = "" />
  •  
  •  
  • <!---
  • There's a number of parts of this workflow that could fail.
  • And, depending on where it fails, we may have to clean up the
  • Authorization that we have created. As such, wrap the workflow
  • in a Try/Catch so we have an opportunity to clean up.
  • --->
  • <cftry>
  •  
  •  
  • <!--- STEP 1: Get an authorization token. --->
  •  
  • <!---
  • Each token is associated with its own set of permissions.
  • For this demo, all we want to do is get "gist" authorization
  • (needed for Create permissions).
  • --->
  • <cfset postBody = {} />
  • <cfset postBody[ "scopes" ] = [ "gist" ] />
  • <cfset postBody[ "note" ] = "For single-use GitHub Gist creation." />
  •  
  • <!---
  • Get GitHub API Authorization. Note that we are using basic
  • authentication to obtain the authorization token.
  • --->
  • <cfhttp
  • result="apiRequest"
  • method="post"
  • url="#baseAPI#authorizations"
  • username="#username#"
  • password="#password#">
  •  
  • <cfhttpparam
  • type="body"
  • value="#serializeJSON( postBody )#"
  • />
  •  
  • </cfhttp>
  •  
  • <!--- Check to see if the API response failed. --->
  • <cfif !reFindNoCase( "2\d\d", apiRequest.statusCode )>
  •  
  • <!--- Failed to create authorization - stop processing. --->
  • <cfthrow type="AuthorizationFailed" />
  •  
  • </cfif>
  •  
  • <!--- Extract the API response payload. --->
  • <cfset authorization = deserializeJSON( apiRequest.fileContent ) />
  •  
  • <!--- Get the authorization data. --->
  • <cfset authorizationID = authorization.id />
  • <cfset authorizationToken = authorization.token />
  •  
  •  
  • <!--- STEP 2: Create the Gist. --->
  •  
  • <!--- Define the gist and its files. --->
  • <cfset postBody = {} />
  • <cfset postBody[ "description" ] = "oAuth exploration." />
  • <cfset postBody[ "public" ] = true />
  • <cfset postBody[ "files" ] = {} />
  • <cfset postBody[ "files" ][ "file1.txt" ] = {} />
  • <cfset postBody[ "files" ][ "file1.txt" ][ "content" ] = "Woot!" />
  • <cfset postBody[ "files" ][ "file2.txt" ] = {} />
  • <cfset postBody[ "files" ][ "file2.txt" ][ "content" ] = "Blam!" />
  •  
  • <!---
  • Post the Gist using the recently procured authorization
  • token. Rather than using Basic Authentication (as we did
  • above), we'll pass the oAuth token in the URL.
  • --->
  • <cfhttp
  • result="apiRequest"
  • method="post"
  • url="#baseAPI#gists">
  •  
  • <cfhttpparam
  • type="url"
  • name="access_token"
  • value="#authorizationToken#"
  • />
  •  
  • <cfhttpparam
  • type="body"
  • value="#serializeJSON( postBody )#"
  • />
  •  
  • </cfhttp>
  •  
  • <!--- Check to see if the API response failed. --->
  • <cfif !reFindNoCase( "2\d\d", apiRequest.statusCode )>
  •  
  • <!--- Failed to create gist - stop processing. --->
  • <cfthrow type="GistFailed" />
  •  
  • </cfif>
  •  
  •  
  • <!--- Catch any errors. --->
  • <cfcatch>
  • <!---
  • Nothing to do here - just need to make sure we don't
  • break out of the page flow before we have a chance to
  • clean up the Authorization.
  • --->
  • </cfcatch>
  •  
  •  
  • </cftry>
  •  
  •  
  • <!---
  • Now that the primary workflow has concluded, let's check to see
  • if we need to clean-up after ourselves. In this case, we only
  • need to do something if the Authorization Token was created.
  • --->
  • <cfif len( authorizationID )>
  •  
  • <!---
  • Delete the authorization token. This, again, uses Basic
  • Authentication.
  • --->
  • <cfhttp
  • result="apiRequest"
  • method="delete"
  • url="#baseAPI#authorizations/#authorizationID#"
  • username="#username#"
  • password="#password#"
  • />
  •  
  • </cfif>

NOTE: My username and password variables have been define in my Application.cfc.

Overall, the code is fairly straightforward; but, between the SSL problems and using a multi-step workflow, I thought it might be worth sharing. Definitely take all of this with a grain of salt as this was the explicitly the quick and dirty approach.

Tweet This Groovy post by @BenNadel - Getting GitHub's v3 API To Work With oAuth Thanks my man — you rock the party that rocks the body!



Reader Comments

@Steve,

Ha ha, glad you like :) Once of these days, I wanna give this baby-beast a full on makeover.

Reply to this Comment

Looks good.

You don't need authorization to post a Gist. It's 100% optional. The only reason to have it is to tie it to your own personal account.

Also, If you don't want to cache the token, maybe you can cache the authorization ID? Then you don't have to create/delete new tokens, you can just call GET authorizations:id.

Reply to this Comment

@Jonathan,

Right - good point to bring up. I definitely wanted the Gists to be associated with my GitHub account. That just felt good :)

Reply to this Comment

@Ben - Hey man, I think I've posted a comment on another blog post relating to this but I'm in love with this idea of "unobtrusive gists" for bloggers. I don't understand why this isn't a WordPress plugin, etc.

It just seems ass backwards creating a gist first and then having to post a [gist id="123123"] in your blogpost and then parsing it.

Mainly just saying kudos to you for doing this because NOBODY ELSE is.

So much easier to post your code in your blog post and have some sort of magic happening behind the scenes handling the conversion to gist.

Reply to this Comment

@Joshua,

Thanks my man! I appreciate that. One of the side-effects that I really like about this approach is that if GitHub goes down or changes their authentication approach, my blog post will still render WITH code; the code just won't be nicely color coded - it will be just black-and-white.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.