Skip to content

Services

Stephen Vickers edited this page Jul 31, 2023 · 15 revisions

The class library provides support for the following services:

  • Outcomes (Basic Outcomes service in LTI 1.1 and the unofficial Outcomes extension service, as well as the Result and Score services included in the Assignment and Grade services specification)
  • Tool Settings (including the unofficial Setting extension service)
  • Memberships/Names and Role Provisioning services (including the Course Groups service and the unofficial Memberships extension service)
  • Course Groups
  • Line Item (including sending and receiving outcomes using the Result and Score services)
  • Assessment Control (included in the Proctoring Services specification)

In order to submit a service request, the related object is required. For example, the Outcomes service requires the related resource link instance. If the service requests are to be made during a user's session, then retaining the primary key of the resource link (obtained from the getRecordId() method) in a session variable is recommended. This would then allow the resource link instance to be opened as follows:

$resourceLink = LTI\ResourceLink::fromRecordId($_SESSION['resource_link_pk'], $dataConnector);

Otherwise using the platform record ID (saved in the session when the user first launched) and the resource link ID is likely to be the most convenient mechanism:

$platform = LTI\Platform::fromPlatformId($_SESSION['platform_pk'], $dataConnector);
$resourceLink = LTI\ResourceLink::fromPlatform($platform, $resourceLinkId);

Context objects can be initialised in a similar manner if a context ID was passed by the platform:

$context = LTI\Context::fromRecordId($_SESSION['context_pk'], $dataConnector);

or

$context = LTI\Context::fromPlatform($platform, $contextId);

Paging Mode

When services are called via methods provided in other classes (such as Context or ResourceLink) all elements will be returned. However, requests made to the platform may be limited to responses of a specified size by setting the $defaultLimit static property of the class in advance. For example, the following code requests that a platform include no more than 50 users in each response:

use ceLTIc\LTI\Service;

$url = $context->getSetting('custom_context_memberships_v2_url');
Service\Membership::$defaultLimit = 50;
$users = $context->getMemberships();

Setting a limit means that these methods may make multiple requests to do so, and not that only a single page will be returned.

If you wish to manage service requests on a page-by-page basis then the service should be called directly with the pageMode property set to true. For example:

use ceLTIc\LTI\Service;

$url = $context->getSetting('custom_context_memberships_v2_url');
$format = Service\Membership::MEDIA_TYPE_MEMBERSHIPS_NRPS;
$limit = 50;
$pagingMode = true;

$membershipService = new Service\Membership($context, $url, $format, $limit, $pagingMode);
$users = $membershipService->get();

Since the pagingMode was set to true, the $users array will contain the first 50 users from the platform rather than the entire set. Any remaining users can be retrieved by sending requests to the next relative link included in each response. For example:

$url = $context->getSetting('custom_context_memberships_v2_url');
$format = Service\Membership::MEDIA_TYPE_MEMBERSHIPS_NRPS;
do {
  $membershipService = new Service\Membership($context, $url, $format, $limit, true);
  $httpMessage = $membershipService->getHttpMessage();
  $users = $membershipService->get();
  /*
    Process next page of users here
  */
  $url = $httpMessage->getRelativeLink('next');
  $limit = null;
} while ($url);

HTTP clients

Requests are sent using an HTTP client. The library includes two implementations of the Http\ClientInterface:

  • CurlClient - uses the Curl library
  • StreamClient - uses a file stream By default, Curl is used if it is available. This can be overridden by setting the client; for example:
uses ceLTIc\LTI\Http;

Http\HttpMessage::setHttpClient(new Http\StreamClient());

The Curl client has an option to override the default HTTP version; for example:

Http\CurlClient::$httpVersion = CURL_HTTP_VERSION_1_1;

LTI 1.3

LTI 1.3 does not sign each service request; instead an access token must be obtained which is then passed to authorise each service request. The request for an access token involves a signed JWT for which a private key is required. In addition, the access token may be requested for use with more than one service. This library works on the assumption that a single access token will be used with each platform, so the tokens will be requested for all the services a tool might use.

Updating the tool class definition

The additional data required for LTI 1.3 can be defined by extending the Tool class which will already exist for processing launch messages. For example:

use ceLTIc\LTI;

class MyApp extends LTI\Tool {

    function __construct($dataConnector) {

        parent::__construct($dataConnector);
...
        $this->baseUrl = 'https://tool.com';
        $this->signatureMethod = 'RS256';
        $this->kid = 'ep4ldfldgljl';
        $this->rsaKey = <<< EOD
-----BEGIN RSA PRIVATE KEY-----
Insert private key here
-----END RSA PRIVATE KEY-----
EOD;
      $this->requiredScopes = array(
          LTI\Service\LineItem::$SCOPE,
          LTI\Service\Score::$SCOPE,
          LTI\Service\Result::$SCOPE
      );

    }
...
}
...
// Define the default tool
LTI\Tool::$defaultTool = new MyApp();

The kid property can be any string and is only required if the tool offers a JWKS endpoint from which platforms can retrieve the tool's public key. The private key may be set by reference to a value defined elsewhere; it is important to keep this value secure. The requiredScopes is an array of all the scopes used by the tool so that access token requests can be appropriately scoped. If left empty, only the scope of the service being used will be requested, so if another service is used later, another access token will have to be requested. When making service requests via the methods built into this library, the platform property of the default tool will automatically be set to the platform corresponding to the object for which the service request is being made. But when using a service class directly be sure to set the platform property before making the call; for example:

LTI\Tool::$defaultTool->platform = $platform;

Platforms may either be provided with the tool's public key or a JSON Web Key endpoint can be added to the tool - see the jwks.php file in the sample Rating application for sample code which implements this functionality.

Access tokens

When a service request is to be made any existing, unexpired access token obtained from the platform will be used. If the request fails (or if there is no access token to use) a new access token will be requested. The request will include the scope for the service being used as well as any other scopes specified requiredScopes property of the default tool (see above). In that way the access token should be usable by any service request the tool makes until it expires. If this also fails then an access token is requested for the specific scope required in case the previous request was rejected because the platform did not accept all of the requested scopes and rejected them all as a result. No methods are provided for using the access token service directly as this is all handled automatically by the library when a service request is made.

The IMS specification does not prescribe a scope for older services so the following are recommended:

Clone this wiki locally