Skip to content

pongr/diamondhead

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Utilities for working with signed requests in Scala. Diamondhead provides low-level generating and parsing functions for signed requests, and makes it easy to handle your own payload JSON format. Diamondhead ships with support for Facebook's signed request format built on these low-level functions.

sbt

Diamondhead releases are in the central Maven repositories.

"com.pongr" %% "diamondhead" % "0.9.0"

Signed Requests

Web services need to communicate with one another. When one web service receives an HTTP request from another which includes important data, that web service often needs to verify that the HTTP request did in fact originate from the expected client (and not some bad actor trying to trick the service). If some other web site is asking your service for HTML to display in an iframe, or notifying you that some event occurred, you need to verify that the requestor is who you expect.

Signed requests provide a simple way for a web service to verify that the information it received in an HTTP request actually came from the expected client. The two services share some secret key (that no one else should know). When one service makes a request to the other, it signs the data it's sending with this secret key (using somethign like HMAC-SHA256), and includes the signature along with this data. When the other service receives the data it verifies the signature.

Note that signed requests do not encrypt the payload data; the data is sent as-is over whatever communication channel is being used. Signed requests simply provide a way for a web service to verify that the data in the request came from the expected client. You should always send HTTP requests via SSL/HTTPS if you want their contents encrypted.

Signed requests are defined in a (somewhat obscure) OAuth2-related spec and used increasingly by Facebook, especially when requesting the iframe content for a Facebook app's canvas page or a Facebook page tab. The base64url encoding and decoding, along with HMAC-SHA256 signing to verify signatures, is tedious code to write that can easily be performed by a library. Diamondhead provides all of this for you.

Facebook Signed Requsts

Diamondhead ships with support for Facebook signed requests, which comes in very handy when your Scala web app needs to verify the signed_request parameter and extract out the Facebook userId and OAuth2 access token. The SignedRequest case class includes common fields that Facebook uses in their signed requests. For examples of how Facebook uses signed requests, see the canvas tutorial and page tab tutorial.

Just call the parse function with your Facebook app secret and the signed request String, and you'll either get back an error or the parsed SignedRequest object:

import com.pongr.diamondhead.facebook._

val signedRequest: String = ??? //probably extracted from a POST request from Facebook
val appSecret: String = ???     //probably from your app config or database
parse(appSecret, signedRequest) match {
  case Right(SignedRequest(_,_,_, Some(userId), Some(token), _,_)) => //authed user
  case Right(sr) => //un-authed user
  case Left(t) => //unable to parse the signed request
}

This SignedRequest case class is built on Diamondhead's generic signed request functions. You can define your own case class to work with your own payload data format.

Custom JSON Protocols

Signed requests aren't just for Facebook. You can define your own payload JSON formats and use them when providing your own services. Typically you want to use a case class to work with payload data, instead of raw json. Simply define a spray-json protocol for your case class:

import spray.json._

object ThingProtocol extends DefaultJsonProtocol {
  case class Thing(a: String, b: Int, c: Boolean)
  implicit val thingFormat = jsonFormat3(Thing)
}

The generate function will convert your case class to json, base64url encode it, sign it and produce the final signed request string:

import com.pongr.diamondhead._

val key = "SomeSecretValue"
val thing = Thing("a", 1, true)
val signedRequest = generate(key, thing)
//signedRequest: String = _W12Hk6kco_wIvlxJcU_u72-nrer2mC1yGi9Fq42dfQ.eyJhIjoiYSIsImIiOjEsImMiOnRydWV9
//now send signedRequest to some web service, they'll know it came from you

The parseAs function will verify the signature in a signed request string, then decode and parse it into an instance of your case class (or return any error that occurred during this process):

import com.pongr.diamondhead._
import ThingProtocol._

val e: Either[Throwable, Thing] = parseAs(key, signedRequest) //use values from above
//e: Either[Throwable,Thing] = Right(Thing(a,1,true))
//handle the parse error or use the thing

Low-Level Functions

The custom JSON protocol functions descried above are built on top of low-level String functions. If by chance you have your own base64url encoded payload String (instead of a case class instance) you can generate the signed request String:

import com.pongr.diamondhead._

val payload: String = ??? //already base64url encoded
val signedRequest: String = generate(key, payload)
//now send signedRequest to some web service, they'll know it came from you

Similarly, if you just need to verify and extract the encoded payload String from a signed request, you can do that too:

import com.pongr.diamondhead._

val e: Either[Throwable, String] = parse(key, signedRequest)
//handle the parse error or use the payload string

License

Diamondhead is released under the Apache 2 License.

Credits

Authors

About

Utilities for working with signed requests in Scala, including support for Facebook signed requests.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages