Question

I have visited and read all the Valence, and specifically the REST API, pages. I have one approved key already and a second key that has not yet been approved by D2L, and it's not clear how I request that approval.

The documentation contains a lot of information, but it is difficult to put all the pieces together. For example, in order to make any REST API call, I have to add several parameters to the end of the call. The parameters are documented in one place, but it isn't clear in some cases how to construct them (for example, one of the keys is to contain the url, timestamp, and the type of call being made, but how are they to be concatenated?). Then they have to be signed, and the documentation that tells how to sign the keys is in a completely different page that is not even referenced from the page that tells you that you have to sign the parameters. On top of that, the documentation is not extremely clear about how to do the signing, and offers no further explanation or examples. So to get anywhere, we have to jump around a lot through the documentation, and go through a whole lot of trial and error. It appears that the documentation assumes that the reader has expertise in several areas, which may or may not be true.

Code examples would make a huge difference.

Was it helpful?

Solution

There aren’t a lot of samples yet; we are working to add more, and to make the ones that are present more obvious. As one example, there is a Java Android app that has all the authentication stuff and some basic calls (including the call “whoami” which is a great test call).

The specific auth related files are available as well. From the D2LSigner class, you can see the signing algorithm we use:

Mac hmacSha256 = Mac.getInstance("hmacSHA256");
byte[] keyBytes = key.getBytes("UTF-8");                 
Key k = new SecretKeySpec(keyBytes, "hmacSHA256");

hmacSha256.init(k);

byte[] dataBytes = data.getBytes("UTF-8");
byte[] sig = hmacSha256.doFinal(dataBytes)

String sigString = base64Url( sig );

From D2LOperationSecurityImpl, you can see how the query string fits together:

//uppercase METHOD, lowercase PATH, timestamp as string 
private static /*final*/ String BASE_STRING_TEMPLATE = "{0}&{1}&{2}"; 

private static /*final*/ String APP_ID_QUERY_NAME = "x_a";
private static /*final*/ String APP_SIG_QUERY_NAME = "x_c";
private static /*final*/ String USER_ID_QUERY_NAME = "x_b";
private static /*final*/ String USER_SIG_QUERY_NAME = "x_d";
private static /*final*/ String TIMESTAMP_QUERY_NAME = "x_t";

...

@Override
public Uri createAuthenticatedUri(String path, String httpMethod) {

    long timestamp = System.currentTimeMillis() +
                     mServerSkewCorrectionMillis.longValue();

    Long timestampObjectSeconds = new Long(timestamp/1000);
    Object[]formatParms = {httpMethod.toUpperCase(),
                           path.toLowerCase(),
                           timestampObjectSeconds.toString()};

    String signatureBaseString = MessageFormat.format(BASE_STRING_TEMPLATE,
                                                      formatParms); 

    String appSig = D2LSigner.base64URLSig(mAppKey, signatureBaseString);
    String userSig = D2LSigner.base64URLSig(mUserKey, signatureBaseString);

    if ((appSig == null) || (userSig == null)) {
        return null;
    }

    String scheme = mEncryptOperations?ENCRYPED_SCHEME:PLAIN_SCHEME;
    Uri.Builder b  = new Uri.Builder();

    b.scheme(scheme);
    b.authority(mHostName);
    b.path(path);
    b.appendQueryParameter(APP_ID_QUERY_NAME, mAppID);
    b.appendQueryParameter(APP_SIG_QUERY_NAME, appSig);
    b.appendQueryParameter(USER_ID_QUERY_NAME, mUserID);
    b.appendQueryParameter(USER_SIG_QUERY_NAME, userSig);
    b.appendQueryParameter(TIMESTAMP_QUERY_NAME, timestampObjectSeconds.toString());

    Uri securedURI = b.build();

    return securedURI;
}

Also, you need to sign the first URL you use for logging in, but only with the application key (because you haven't yet established a user context). It uses a different base string (to protect the URL that is used during auth):

String signature = D2LSigner.base64URLSig(mAppKey, resultURLString);
BasicNameValuePair appID = new BasicNameValuePair(APP_ID_NAME, mAppID);
BasicNameValuePair appSig = new BasicNameValuePair(APP_SIG_NAME, signature);
BasicNameValuePair callbackURL = new BasicNameValuePair(CALLBACK_NAME, resultURLString);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top