Search code examples
javascriptauthenticationsingle-sign-onlinkedin-apilti

LinkedIn Learning LTI failed authentication


I'm attempting to integrate LinkedIn Learning Single-Sign-On via an LTI connection, however I'm always faced with the response: LTI_FAILED_AUTHENTICATION.

LinkedIn Learning - LTI_FAILED_AUTHENTICATION

When I test it out on the Saltire test platform, it strangely works.

The parameters match what I am sending from the code below: Saltire LTI Success authentication

Have tried copying over the the values of oauth_nonce, timestamp and oauth_signature from Saltire to my page, and that worked also, which scores out the possibility of domain whitelisting requirement.

LinkedIn support have come back saying there seems to be something wrong with the generated signature, but I'm not sure what is wrong about it, since that is generated by the parameters passed.

Is there something incorrectly setup from my page which I am not seeing?

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="robots" content="noindex" />
    <title>Access LinkedIn Learning</title>
    <script src="bundle.js"></script>
</head>

<body>
    <form id="id_frmConnect" name="frmConnect" enctype="application/x-www-form-urlencoded">
    </form>

    <script>
        var oauth = require('oauth-sign');
        var action = 'https://www.linkedin.com/checkpoint/enterprise/login/[accountID]?application=learning&redirect=https://www.linkedin.com/learning/me';
        var method = 'POST';
        var consumer_key = '************';
        var consumer_secret = '************';
        var timestamp = Math.round(Date.now() / 1000);

        var params = {
            lti_message_type: 'basic-lti-launch-request',
            lti_version: 'LTI-1p0',
            oauth_callback: 'about:blank',
            oauth_consumer_key: consumer_key,
            oauth_nonce: btoa(timestamp),
            oauth_signature_method: 'HMAC-SHA1',
            oauth_timestamp: timestamp,
            oauth_version: '1.0',
            user_id: 'S495696'
        };

        var signature = oauth.hmacsign(method, action, params, consumer_secret);
        params.oauth_signature = signature;

        var form = document.querySelector("#id_frmConnect");
        form.action = action;
        form.method = method;
        for (var name in params) {
            var node = document.createElement("input");
            node.type = 'hidden';
            node.name = name;
            node.value = params[name];
            form.appendChild(node);
        }
    </script>
</body>

</html>

Solution

  • I figured out the issue. By using the Saltire test tool, I was able to verify that my signature was generated correctly when using their testing URL: https://lti.tools/saltire/tp

    You can play with an example here: https://learningcom.github.io/ltitest/index.html

    So after looking at the LinkedIn URL, I discovered that the signature was getting generated with an unnecessary long URL which contained parameters.

    Removed: ?application=learning&redirect=https://www.linkedin.com/learning/me

    Therefore, I shortened the URL to:

    var action = 'https://www.linkedin.com/checkpoint/enterprise/login/[accountID]';

    No more errors!