0
votes

I am trying to create an external learning tool within Desire2Learn by following the instructions at https://d2l.nl.edu/shared/HelpFiles/10%20Administrator%20Help/learningenvironment/basic_lti_standards_support/org_level_lti_configuration.html and http://devs.valence.desire2learn.com/tag/lti and can't even get the sample quiz project working.

I've tried everything that I can think of and at one point I had it working and could properly load my learning tool in Desire2Learn, but it seems to have just stopped and I can't figure it out anymore as I've spent 5 hours trying to get around this. I've attempted such things as echo'ing out the POST and local variables to debug and throwing different things in the URL fields inside the tool provider and link fields in Desire2Learn.

All that I have noticed in my 5 hours I've spent on this is when the signature is recreated during the authentication process, it never matches the [oauth_signature] field sent in the POST data, despite the key and secret matching those in the LMS.

If this makes sense, I appreciate any and all help I can get on this as I've spent way too much time on this already and can't think straight anymore. Thank you!

EDIT:

Here is a print_r() of the context data Desire2Learn sends back along with the OAUTH parameters I'm validating against, and below it the local variables I set.

[launch_presentation_locale] => EN-US__
    [tool_consumer_instance_guid] => key
    [tool_consumer_instance_name] => secret
    [tool_consumer_instance_description] => Learning Tool.
    [tool_consumer_instance_contact_email] => 
    [tool_consumer_info_version] => 10.3.0 SP5
    [tool_consumer_info_product_family_code] => desire2learn
    [context_id] => 2440554
    [context_title] => context
    [context_label] => context
    [context_type] => 
    [user_id] => userid_88888
    [roles] => None
    [ext_tc_profile_url] => https://sampleprofileurl.com
    [ext_d2l_token_id] => 386867949
    [ext_d2l_link_id] => 9554
    [ext_d2l_token_digest] => 2bv8al0f+NxuBWpBS36nl/RuNWg=
    [resource_link_id] => 
    [resource_link_title] => LinkTitle
    [resource_link_description] => LinkDes.
    [lis_result_sourcedid] => 808080-89989-234232
    [lis_outcome_service_url] => https://outcomeurl.com
    [lti_version] => LTI-1p0
    [lti_message_type] => basic-lti-launch-request
    [oauth_version] => 1.0
    [oauth_nonce] => 784335425
    [oauth_timestamp] => 1403634776
    [oauth_signature_method] => HMAC-SHA1
    [oauth_consumer_key] => key
    [oauth_callback] => about:blank
    [oauth_signature] => PYCJyQe3jSLTXn8vxet1eknSfoc=
    [basiclti_submit] => Launch Endpoint with BasicLTI Data

$OAUTH_KEY    = 'key';
$OAUTH_SECRET = 'secret'; 
$SITE_URL     = 'https://sample.com';

When the POST data is initially sent back from Desire2Learn, $OAUTH_KEY is checked against the $_POST['oauth_consumer_key'] field and if they're equal, the $OAUTH_KEY and $OAUTH_SECRET fields are used to generate a temporary signature/token that is then authenticated against the $_POST['oauth_signature'] field.

public static function CheckSignatureForFormUrlEncoded( $url, $httpMethod, $parameters, $secret) {
        $oauthParameters = array();
        $lmsParameters = array();

        // Separate LMS and OAuth parameters
        foreach( $parameters as $key => $value ) {
            if( strpos( $key, self::OAUTH_PREFIX ) === 0 ) {
                $oauthParameters[urldecode( $key )] = urldecode( $value );
                continue;
            }
            $lmsParameters[urldecode( $key )] = urldecode( $value );
        }

        $signature = self::CalculateSignatureForFormUrlEncoded( $url, $httpMethod, $secret, $lmsParameters, $oauthParameters );

        return $parameters[ self::SIGNATURE ] === $signature;   

    }

This is where I'm being held back. When the return statement returns, or in other words the signature value in the $parameters array doesn't match $signature, I get the error "Invalid OAuth Signature". From what I understand, this shouldn't happen if the 'key' and 'secret' fields I set in my LMS are equal to those that I set in the two variables above.

1
"I tried to do X and it doesn't work" is not a useful question. What doesn't work, specifically? Are there error messages? What have you tried to fix them? What code have you written? I want to help but I just can't. - Alex
Sorry, it seemed a little complex to explain. I editted my question to provide the initial POST data that gets sent from Desire2Learn to my learning tool and includes some OAUTH variables that I have to authenticate against. Everything seems fine except the oauth_signature field is giving me problems. - hRdCoder
It's still very difficult to work out what's going on here. What are the preconditions of CaclulateSignatureForFormUrlEncoded? Are the preconditions of CheckSignatureForFormUrlEncoded being upheld? Did any of this come from somewhere or are you rolling your own? - Alex
Sorry for replying so late, but the CalculateSignatureForFormUrlEncoded() method removes unused variables from the $parameters array that is passed to it and then calls another method called CalculateSHA1Hash() that takes in a string and the 'secret' field as an optional parameter and performs a base64_encode using the HMAC_SHA1 algorithm to hash the string. It is this string, then, that is compared with the one passed from the LMS to my program to perform authentication. - hRdCoder

1 Answers

0
votes

Okay, I seem to have figured it out. As dumb as it sounds, all that I had to do was append 'index.php' to the end of the URL field in the link to my external learning tool in Desire2Learn, as well as the launch point field in the tool provider configuration. After I did this the signature error went away and it's all better now. Apparently the tool consumer isn't looking specifically for an index.anything file at all as I had assumed.