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 ) ) } }