Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.
/mod/lti/ -> token.php (source)
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * This file contains a service for issuing access tokens
  19   *
  20   * @package    mod_lti
  21   * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  define('NO_DEBUG_DISPLAY', true);
  26  define('NO_MOODLE_COOKIES', true);
  27  
  28  use Firebase\JWT\JWT;
  29  
  30  require_once(__DIR__ . '/../../config.php');
  31  require_once($CFG->dirroot . '/mod/lti/locallib.php');
  32  
  33  $response = new \mod_lti\local\ltiservice\response();
  34  
  35  $contenttype = isset($_SERVER['CONTENT_TYPE']) ? explode(';', $_SERVER['CONTENT_TYPE'], 2)[0] : '';
  36  
  37  $ok = ($_SERVER['REQUEST_METHOD'] === 'POST') && ($contenttype === 'application/x-www-form-urlencoded');
  38  $error = 'invalid_request';
  39  
  40  $clientassertion = optional_param('client_assertion', '', PARAM_TEXT);
  41  $clientassertiontype = optional_param('client_assertion_type', '', PARAM_TEXT);
  42  $granttype = optional_param('grant_type', '', PARAM_TEXT);
  43  $scope = optional_param('scope', '', PARAM_TEXT);
  44  
  45  if ($ok) {
  46      $ok = !empty($clientassertion) && !empty($clientassertiontype) &&
  47            !empty($granttype) && !empty($scope);
  48  }
  49  
  50  if ($ok) {
  51      $ok = ($clientassertiontype === 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer') &&
  52            ($granttype === 'client_credentials');
  53      $error = 'unsupported_grant_type';
  54  }
  55  
  56  if ($ok) {
  57      $parts = explode('.', $clientassertion);
  58      $ok = (count($parts) === 3);
  59      if ($ok) {
  60          $payload = JWT::urlsafeB64Decode($parts[1]);
  61          $claims = json_decode($payload, true);
  62          $ok = !is_null($claims) && !empty($claims['sub']);
  63      }
  64      $error = 'invalid_request';
  65  }
  66  
  67  if ($ok) {
  68      $tool = $DB->get_record('lti_types', array('clientid' => $claims['sub']));
  69      if ($tool) {
  70          try {
  71              lti_verify_jwt_signature($tool->id, $claims['sub'], $clientassertion);
  72              $ok = true;
  73          } catch (Exception $e) {
  74              $error = $e->getMessage();
  75              $ok = false;
  76          }
  77      } else {
  78          $error = 'invalid_client';
  79          $ok = false;
  80      }
  81  }
  82  
  83  if ($ok) {
  84      $scopes = array();
  85      $requestedscopes = explode(' ', $scope);
  86      $typeconfig = lti_get_type_config($tool->id);
  87      $permittedscopes = lti_get_permitted_service_scopes($tool, $typeconfig);
  88      $scopes = array_intersect($requestedscopes, $permittedscopes);
  89      $ok = !empty($scopes);
  90      $error = 'invalid_scope';
  91  }
  92  
  93  if ($ok) {
  94      $token = lti_new_access_token($tool->id, $scopes);
  95      $expiry = LTI_ACCESS_TOKEN_LIFE;
  96      $permittedscopes = implode(' ', $scopes);
  97      $body = <<< EOD
  98  {
  99    "access_token" : "{$token->token}",
 100    "token_type" : "Bearer",
 101    "expires_in" : {$expiry},
 102    "scope" : "{$permittedscopes}"
 103  }
 104  EOD;
 105  } else {
 106      $response->set_code(400);
 107      $body = <<< EOD
 108  {
 109    "error" : "{$error}"
 110  }
 111  EOD;
 112  }
 113  
 114  $response->set_body($body);
 115  
 116  $response->send();