LTI-compliant LMS Integration Guide

1. Proctoring scenario

The scenario of a test-taker's interaction with the proctoring system and the LTI-compliant LMS in the general case is as follows:

  1. A test-taker logs in to the LMS and opens their course;
  2. Goes to the LTI link or element configured with the proctoring system;
  3. Next opens the page with the proctoring script (Supervisor SDK);
  4. The process of surveillance begins;
  5. The test-taker will see the quiz in IFRAME mode;
  6. The proctoring script opens access to the quiz by password;
  7. The test-taker passes the quiz;
  8. After the quiz, the test-taker is redirected to the course page;
  9. After a while, the proctoring system sends the results to the LMS.
    LTI integration scheme
    LTI integration scheme

2. How to integrate

The integration of the proctoring system with your LMS is done using Learning Tools Interoperability (LTI) and Supervisor SDK (JavaScript proctoring library) in three steps:

  1. Upload the script code in your LMS that:
    1. starts the proctoring session;
    2. injects the password to access the quiz;
    3. stops the proctoring session after the quiz is completed.
  2. Set up your quiz, such as:
    1. protect the quiz by password
    2. get the quiz URL
  3. Configure LTI in your course to access proctoring, such as:
    1. set up an LTI external tool;
    2. add an LTI element to your course with custom parameters.

3. Upload the script code

Save the code below in the “proctoring.html” file on your computer, and then upload it to your LMS or its web server with the same domain as quizzes:

HTML+JS

<!doctype html>
<html lang="en">
<head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <title>Proctoring</title>
</head>
<body>
 <p>There is nothing here. This is a service page for proctoring.</p>
 <script src="//demo.proctoring.app/sdk/supervisor.js" data-supervisor="sync"></script>
 <script>
   if (window.supervisor) {
     supervisor.on('load', function(iframe) {
       const hash = new URL(iframe.src).hash.slice(1);
       if (hash) {
         const { pathname } = iframe.contentWindow.location;
         if (pathname.includes(hash)) {
           supervisor.stop();
         }
       }
     });
   }
 </script>
</body>
</html>

NOTE: you need to replace **demo.proctoring.app** with the domain of your proctoring server in the code.

After you do this, save the full URL to the page for future reference.

4. Set up the quiz

Go to your quiz settings and set up a password to restrict access to this quiz. Your LMS must support this, otherwise your quiz can still be taken without the proctoring guard. You should also copy the full quiz URL. You may need to open this quiz as a test-taker to do this. Save the URL for future reference.

5. Configure LTI external tool

You should set up the LTI external tool for the proctoring service in your LMS and insert the LTI link into your course.

Use the following parameters to set up the LTI:

  • Tool URL: https://demo.proctoring.app/api/auth/lti?redirect=<proctoring.html>

where “<proctoring.html>” should be changed to the full URL of the page from section 3, and “demo.proctoring.app” with the domain of your proctoring server.

  • Consumer Key: demo
  • Consumer Secret: secret
  • Custom Parameters (key=value on separate lines): | Key | Value | | -------- | -------------------------- | | template | default | | members | @ | | url | #/path/to/submit | | code | |

where “<quiz_url>” should be replaced with the full quiz URL and “<quiz_password>” with the quiz password from section 4; optionally, you can replace “/path/to/submit” with the end of the quiz completion URL or just remove it.

6. Set up the proctoring host

To connect the integration API with your LMS, you need to load the following configuration with integration parameters under the proctoring system manager:

JSON

{
 "id": "<Host_ID>",
 "key": "<License_Key>",
 "params": {
   "webhooks": {
     "lti": {
       "authorizer": "lti",
       "integrator": "lti",
       "consumerKey": "demo",
       "consumerSecret": "secret",
       "callbackURL": "query.redirect",
       "profile": {
         "username": "payload.user_id",
         "role": "payload.roles.find(v=>/Instructor/.test(v))?'proctor':'student'",
         "nickname": "payload.lis_person_name_full",
         "lang": "(payload.launch_presentation_locale||'').slice(0,2)",
         "group": "['G',payload.context_id,payload.resource_link_id].join('-')",
         "referrer": "payload.launch_presentation_return_url",
         "labels": "payload.lis_person_contact_email_primary"
       },
       "register": {
         "identifier": "[payload.user_id,payload.context_id,payload.resource_link_id].join('-').replace(/[^A-Za-z0-9_-]+/g,'_')",
         "template": "payload.custom_template||'default'",
         "subject": "payload.resource_link_title",
         "members": "payload.custom_members==='@'?user.group:payload.custom_members",
         "url": "payload.custom_url||payload.launch_presentation_return_url",
         "code": "payload.custom_code",
         "tags": "payload.lis_person_contact_email_primary"
       },
       "start": true,
       "stop": true,
       "pause": true,
       "submit": true
     }
   }
 }
}

NOTE: **consumerKey** and **consumerSecret** fields should be replaced with a randomly generated sequence of characters (Latin letters of different case and numbers, the recommended length is 24 characters); **id** is the ID of the host (if you do not specify it, a new host will be created); **key** is the license key of this host.