Daily Archives: December 27, 2009

Repost: From the IMS Developer’s Forum

This is a repost from the IMS Developer’s forum – I forgot to put this in my own blog.

Possible issues with the OAuth C# library. I was working with some folks on C# Basic LTI and came across a seeming interoperability. Here is the discussion:

I looked at the Java Implementations and the PHP implementation and both seem to go to some length *to* double URL-encode the signature string. The PHP code interoperates with the Java code as distributed.

This results in the base signature string having %2520 where there are spaces. Which looks like a mistaken double-encode – but I think that it is an intentional double encode.

If you look at this page (broken into multiple lines to enhance readiblilty)

http://hueniverse.com/2008/10/beginners-guide-to-oauth-part-iv-signing-requests/

After you pick “Non-URL Safe Parameter” expand the little + signs and follow it through – it ends up with a base string that includes a %2520:

GET&http%3A%2F%2Fphotos.example.net%3A8001%2FPhotos&oauth_consumer_key
%3Ddpf43f3%252B%252Bp%252B%25232l4k3l03%26oauth_nonce
%3Dkllo~9940~pd9333jh%26oauth_signature_method%3DHMAC-SHA1
%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d
%25280%25290sl2jdk%26oauth_version%3D1.0%26photo%2520size%3D300%2525
%26title%3DBack%2520of%2520%2524100%2520Dollars%2520Bill

As I look at the sample Java code below – it is effectively double encoding very much on purpose.

So far, this suggests that it is *correct* when spaces turn into %2520 in the pre-signature string instead of %20. So I am thinking that the the sample VB.NET code from oauth.net *might* be wrong.

Sample Java code

public static String getBaseString(OAuthMessage message)
            throws IOException, URISyntaxException {
  List<Map.Entry<String, String>> parameters;
  String url = message.URL;
  int q = url.indexOf('?');
  if (q < 0) {
    parameters = message.getParameters();
  } else {
    // Combine the URL query string with the other parameters:
    parameters = new ArrayList<Map.Entry<String, String>>();
    parameters.addAll(OAuth.decodeForm(message.URL.substring(q + 1)));
    parameters.addAll(message.getParameters());
    url = url.substring(0, q);
  }
  return OAuth.percentEncode(message.method.toUpperCase()) + '&'
    + OAuth.percentEncode(normalizeUrl(url)) + '&'
    + OAuth.percentEncode(normalizeParameters(parameters));
}

protected static String normalizeParameters(Collection<? extends Map.Entry> parameters)
  throws IOException {
  if (parameters == null) {
    return "";
  }
  List<ComparableParameter> p = new ArrayList<ComparableParameter>(parameters.size());
  for (Map.Entry parameter : parameters) {
    if (!"oauth_signature".equals(parameter.getKey())) {
      p.add(new ComparableParameter(parameter));
    }
  }
  Collections.sort(p);
  return OAuth.formEncode(getParameters(p));
}

public static void formEncode(Iterable<? extends Map.Entry> parameters,OutputStream into) 
  throws IOException {
  if (parameters != null) {
    boolean first = true;
    for (Map.Entry parameter : parameters) {
      if (first) {
        first = false;
      } else {
        into.write('&');
      }
      into.write(percentEncode(toString(parameter.getKey())).getBytes());
      into.write('=');
      into.write(percentEncode(toString(parameter.getValue())).getBytes());
    }
  }
}