Learning Grails the hard way: Facebook Authentication

Learning Grails the hard way: Facebook Authentication

Every now and then I try to explore a new programming language. Last year, I tried Scala, this year, I’m fiddling around with Grails. My first attempt was to try something I already implemented in other languages, like verifying Facebook’s signed_request. It’s fun to see what I tried to get it running and how a short version by a more experienced Groovy coder looks like.

Compare both versions after the break if you like.

First, here’s my attempt. Note the many references to plain old Java methods.

class CanvasController {
    def index = {
		if( params.signed_request == null ) {
			render( 'signed_request is empty!' )
			return
		}
		def dot = params.signed_request.indexOf( "." ) 
		if( dot == -1 ) {
			render( "Invalid signed_request!" )
			return
		}
		// Unescape base64url: '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_',
		def unescapedRequest = params.signed_request.replaceAll( "-", "+" ).replaceAll( "_", "/" )
		def signatureRaw = unescapedRequest.substring( 0, dot )
		def payloadRaw = unescapedRequest.substring( dot + 1 )
		// Padding for base64 decoding
		def jsonBase64 = payloadRaw
		while( jsonBase64.length() % 4 != 0 ) {
			jsonBase64 += "=";
		}
		def signatureBase64 = signatureRaw
		while( signatureBase64.length() % 4 != 0 ) {
			signatureBase64 += "=";
		}
		
		def jsonBytes = jsonBase64.decodeBase64()
		def jsonDecoded = new String( jsonBytes )
		def signatureBytes = signatureBase64.decodeBase64()
		def signature = new String( signatureBytes )
		def json = new JSON().parse( jsonDecoded )
		
		// Check for correct algorithm
		if( json.algorithm != "HMAC-SHA256" ) {
			render( "Unknown algorithm: " + json.algorithm )
			return;
		}
		
		// Check signature
		def mac = Mac.getInstance( "HmacSHA256" )
		mac.init( new SecretKeySpec( ConfigurationHolder.config.app.facebook.secret.getBytes(), "HmacSHA256" ) )
		mac.update( payloadRaw.bytes )
		def calculatedSignature = new String( mac.doFinal() );

		// Render for visual comparison		
		render( calculatedSignature + " --- " + signature + " xxxxxxxxxxxxxxx " )
	}
}

Now here’s the much slimmer version (credits) with some adaptions by me, like logging and setting the HTTP response code, two other things I learned on the way.

class CanvasController {
    def index = {
		def (rawSig, rawPayload) = params.signed_request.split( /./, 2 )
		def (sig,payload) = [ rawSig, rawPayload ]*.tr( '-_', '+/' )*.decodeBase64()
		def data = JSON.parse( new String( payload ) )
		if( data.algorithm != "HMAC-SHA256" ) {
			log.debug( "Unknown algorithm: ${data.algorithm}" )
			response.status = 500
			return
		}
		
		def secret = grailsApplication.config.app.facebook.secret
		def expectedSig = Mac.getInstance( "HmacSHA256" ).with {
			init( new SecretKeySpec( secret.bytes, "HmacSHA256" ) )
			update( rawPayload.bytes )
			doFinal()
		}
		if( sig != expectedSig ) {
			log.debug( "signatures don't match: ${sig.encodeAsBase64()} != ${expectedSig.encodeAsBase64()}" )
			response.status = 500
			return
		}
		
		render( new String( sig ) + " ---- " + new String( expectedSig ) )
	}
}

No Comments

Cancel