Reputation: 1133
I'm trying to implement LTI in our education platform to connect with QTIWoks in Java.
I have a simple tool consumer that generates the following HTML Form:
<html>
<head> </head>
<body>
<form action="http://192.168.0.114:8080/qtiworks-engine/lti/domainlaunch" name="ltiLaunchForm" id="ltiLaunchForm" method="post" target="basicltiLaunchFrame" enctype="application/x-www-form-urlencoded" style="display: block;">
<input type="hidden" name="context_id" value="cid-00113">
<input type="hidden" name="context_label" value="SI106">
<input type="hidden" name="context_title" value="Design of Personal Environments 1">
<input type="hidden" name="ext_note" value="Instructor from first course">
<input type="hidden" name="launch_presentation_locale" value="en_us">
<input type="hidden" name="lis_person_contact_email_primary" value="[email protected]">
<input type="hidden" name="lis_person_name_family" value="Instructor">
<input type="hidden" name="lis_person_name_given" value="Siân">
<input type="hidden" name="lis_person_sourcedid" value="school.edu:user">
<input type="hidden" name="resource_link_description" value="This learning space is private">
<input type="hidden" name="resource_link_id" value="res-0012612">
<input type="hidden" name="resource_link_title" value="My Weekly Wiki">
<input type="hidden" name="roles" value="Instructor">
<input type="hidden" name="tool_consumer_info_product_family_code" value="sakai-unit">
<input type="hidden" name="tool_consumer_info_version" value="0.9">
<input type="hidden" name="tool_consumer_instance_description" value="University of School (LMSng)">
<input type="hidden" name="tool_consumer_instance_guid" value="lmsng.school.edu">
<input type="hidden" name="user_id" value="user-0016">
<input type="hidden" name="oauth_callback" value="about:blank">
<input type="hidden" name="custom_simple_key" value="custom_simple_value">
<input type="hidden" name="custom_complex____________key" value="Complex!@#$^*(){}[]½Value">
<input type="hidden" name="lti_version" value="LTI-1p0">
<input type="hidden" name="lti_message_type" value="basic-lti-launch-request">
<input type="hidden" name="oauth_version" value="1.0">
<input type="hidden" name="oauth_nonce" value="Z2WVNEPUZkzsolOe4hRvKbkXtOSmYiyw">
<input type="hidden" name="oauth_timestamp" value="1429793062">
<input type="hidden" name="oauth_consumer_key" value="feras">
<input type="hidden" name="oauth_signature_method" value="HMAC-SHA1">
<input type="hidden" name="oauth_signature" value="g908qTtVGh8MsOgVUdarVlSBmC0=">
<input type="hidden" name="ext_submit" value="Finish Launch">
</form><iframe name="basicltiLaunchFrame" id="basicltiLaunchFrame" src="" width="100%" height="900" scrolling="auto" frameborder="1" transparency="">
</iframe>
<script type="text/javascript">
document.getElementById("ltiLaunchForm").style.display = "none";
nei = document.createElement('input');
nei.setAttribute('type', 'hidden');
nei.setAttribute('name', 'ext_submit');
nei.setAttribute('value', 'FinishLaunch');
document.getElementById("ltiLaunchForm").appendChild(nei);
document.ltiLaunchForm.submit();
</script>
</body>
</html>
then it calls the dominlaunch from qtiworks
@RequestMapping(value="/domainlaunch", method=RequestMethod.POST)
public String ltiDomainLevelLaunch(final HttpSession httpSession, final HttpServletRequest request,
final HttpServletResponse response)
throws IOException {
/* Decode LTI launch request, and bail out on error */
final DecodedLtiLaunch decodedLtiLaunch = ltiLaunchService.decodeLtiLaunchData(request, LtiLaunchType.DOMAIN);
if (decodedLtiLaunch.isError()) {
response.sendError(decodedLtiLaunch.getErrorCode(), decodedLtiLaunch.getErrorMessage());
return null;
}
final LtiLaunchData ltiLaunchData = decodedLtiLaunch.getLtiLaunchData();
/* Make sure this is a domain launch */
final LtiUser ltiUser = decodedLtiLaunch.getLtiUser();
final LtiDomain ltiDomain = ltiUser.getLtiDomain();
if (ltiDomain==null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "The tool consumer has attempted a domain-level launch using a link-level key");
return null;
}
/* Extract/create the corresponding LtiResource for this launch */
final LtiResource ltiResource = ltiLaunchService.provideLtiResource(decodedLtiLaunch); /* (May be null for candidates) */
final UserRole userRole = ltiUser.getUserRole();
if (userRole==UserRole.INSTRUCTOR) {
/* If user is an instructor, we'll forward to the LTI instructor MVC after
* "authenticating" the user by creating and storing an LtiDomainTicket
* in the session */
final LtiAuthenticationTicket ltiDomainTicket = new LtiAuthenticationTicket(ltiUser.getId(), ltiResource.getId(),ltiLaunchData.getLaunchPresentationReturnUrl());
LtiResourceAuthenticationFilter.authenticateUserForResource(httpSession, ltiDomainTicket);
return "redirect:/lti/resource/" + ltiResource.getId();
}
else if (userRole==UserRole.CANDIDATE) {
/* If user is a candidate, then we'll launch/reuse a candidate session */
if (ltiResource==null) {
return "candidateLaunchError";
}
/* Extract relevant data */
final String returnUrl = ltiLaunchData.getLaunchPresentationReturnUrl();
final String lisOutcomeServiceUrl = ltiLaunchData.getLisOutcomeServiceUrl();
final String lisResultSourcedid = ltiLaunchData.getLisResultSourcedid();
/* Launch and redirect to session */
try {
final CandidateSessionTicket candidateSessionTicket = candidateSessionLaunchService.launchDomainLevelLtiCandidateSession(httpSession,
ltiUser, ltiResource, returnUrl, lisOutcomeServiceUrl, lisResultSourcedid);
return GlobalRouter.buildSessionStartRedirect(candidateSessionTicket);
}
catch (final CandidateException e) {
return "candidateLaunchError";
}
}
else {
throw new QtiWorksLogicException("Unexpected LTI userRole " + userRole);
}
}
the problem is decodedLtiLaunch.isError()
returns true and my request is not performed.
I debugged the problem and found that HttpServletRequest request
has no item in parameterMap
however, it's working well when the request comes from moodle (request's parameterMap has the passed params)
how can I solve this please?
thanks in advance.
Upvotes: 1
Views: 321
Reputation: 3367
Without knowing what the exact error code you are seeing is, I am going to make a very strong guess that your signature is bad
Since you are using JAVA I would suggest that you utilize the basiclti-util library that IMSGlobal provides to generate a signature
Add the following dependency
<dependency>
<groupId>org.imsglobal</groupId>
<artifactId>basiclti-util</artifactId>
<version>1.1.2</version>
</dependency>
Then use the following code to generate the signature
Map<String, String> signedParameters = new LtiOauthSigner()
.signParameters(parameters, key, secret, url, "POST");
Then take all of the key-value pairs in the signedParameters map, and use that to construct the form input tags you have in your example.
Upvotes: 0