So I really liked Scala when I worked with it in the Seven Languages book. I've also been meaning to write an OAuth library for some time. I decided that this would be an interesting first project in Scala. It's not overly complex, but not so simple that I won't learn anything either.

So far I've written just enough to make the first request for a request_token from an OAuth provider. I used traits, treating them more or less like a Java abstract class, to create classes to sign the messages using HMAC-SHA1 or plaintext methods and will be adding the RSA-SHA1 option eventually.

There seems to be a lot of confusion on how to generate the HMAC-SHA1 signature in Java (and so Scala). I saw several different examples of what to do and none of them worked. The code below is what I eventually came to. This has worked to authenticate to both Twitter and Yahoo.

Sorry for the funky formatting. I need to change my CSS up plus copying and pasting formatted text in and then formatting with markdown just gets a git off at times.

package jm.oauth.messagesigner

import jm.oauth.MessageSigner
import java.net.URLEncoder
import org.apache.commons.codec.digest.DigestUtils //nicer implementation to work with than java.security.MessageDigest
import org.apache.commons.codec.binary.Hex
import org.apache.commons.codec.binary.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import scala.collection.immutable.SortedMap

class HmacSha1 extends MessageSigner{

    //May compact the url, method, and params to a single object
    override def createSignature(key: String, token: String, method: String, url: String, requestParams: Map[String, String]): String = {
      //First create a SortedMap which is sorted on the key from our Map
      //and then feeds that into map() to combine key and value, then into reduce to join each k,v pair with an &
      val sorted = SortedMap(requestParams.toList:_*)
      val sigString = method.toUpperCase() + "&" + URLEncoder.encode(url) + "&" + 
            URLEncoder.encode(sorted.map(p => p._1 + "=" + p._2).reduceLeft{(joined,p) => joined + "&" + p})

        return new String(Base64.encodeBase64(generateSHA1Hash(sigString, key, token).getBytes()))
    }

    def generateSHA1Hash(value: String, key: String, token: String): String = {

      //When token is null it gets cast to the string "null" if it is just concatenated it in here
      val keyString = URLEncoder.encode(key) + "&" + (token match {
        case x if x != null => token
        case x => ""
      })

      val keyBytes = keyString.getBytes();           
      val signingKey = new SecretKeySpec(keyBytes, "HmacSHA1")
      val mac = Mac.getInstance("HmacSHA1");
      mac.init(signingKey);
      val rawHmac = mac.doFinal(value.getBytes());
      return new String(rawHmac)
  }
}

The key problem left to this part is that apparently the OAuth spec does not say what should be returned from the request to get the request_token. Twitter and Yahoo both return different things, which makes it a bit difficult to create a generic return value. I was going to just return a Tuple of the values, but I will probably have to return a Map instead.

If you want to follow this, I've got the code up on my git repo and if I take it far enough to be useful, will probably also put it on github eventually.