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>
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!