2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * LTI Advantage Initiate Dynamic Registration endpoint.
20 * https://www.imsglobal.org/spec/lti-dr/v1p0
22 * This endpoint handles the Registration Initiation Launch, in which a platform (via the user agent) sends their
23 * OpenID config URL and an optional registration token (to be used as the access token in the registration request).
25 * The code then makes the required dynamic registration calls, namely:
26 * 1. It fetches the platform's OpenID config by making a GET request to the provided OpenID config URL.
27 * 2. It then POSTS a client registration request (along with the registration token provided by the platform),
29 * Finally, the code returns to the user agent signalling a completed registration, via a HTML5 web message
30 * (postMessage). This lets the browser know the window may be closed.
33 * @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 use core\context\system
;
38 use enrol_lti\local\ltiadvantage\repository\application_registration_repository
;
39 use enrol_lti\local\ltiadvantage\repository\context_repository
;
40 use enrol_lti\local\ltiadvantage\repository\deployment_repository
;
41 use enrol_lti\local\ltiadvantage\repository\resource_link_repository
;
42 use enrol_lti\local\ltiadvantage\repository\user_repository
;
43 use enrol_lti\local\ltiadvantage\service\application_registration_service
;
45 require_once(__DIR__
."/../../config.php");
46 global $OUTPUT, $PAGE, $CFG, $SITE;
47 require_once($CFG->libdir
. '/filelib.php');
49 $PAGE->set_context(context_system
::instance());
50 $PAGE->set_pagelayout('popup');
52 // URL to the platform's OpenID configuration.
53 $openidconfigurl = required_param('openid_configuration', PARAM_URL
);
55 // Token generated by the platform, which must be sent back in registration request.
56 $regtoken = required_param('registration_token', PARAM_RAW
);
57 if (!preg_match('/[a-zA-Z0-9-_.]+/', $regtoken)) { // 0 on no match, FALSE on error.
58 throw new coding_exception('Invalid registration_token.');
61 // Moodle-specific token used to secure the dynamic registration URL.
62 $token = required_param('token', PARAM_ALPHANUM
);
64 $appregservice = new application_registration_service(
65 new application_registration_repository(),
66 new deployment_repository(),
67 new resource_link_repository(),
68 new context_repository(),
72 // Using the application registration repo, find the incomplete registration using its unique id.
73 $appregrepo = new application_registration_repository();
74 $draftreg = $appregrepo->find_by_uniqueid($token);
75 if (is_null($draftreg) ||
$draftreg->is_complete()) {
76 throw new moodle_exception('invalidexpiredregistrationurl', 'enrol_lti');
79 // Get the OpenID config from the platform.
81 $openidconfig = $curl->get($openidconfigurl);
82 $errno = $curl->get_errno();
84 throw new coding_exception("Error '{$errno}' while getting OpenID config from platform: {$openidconfig}");
86 $openidconfig = json_decode($openidconfig);
87 if (json_last_error() !== JSON_ERROR_NONE
) {
88 throw new moodle_exception('ltiadvdynregerror:invalidopenidconfigjson', 'enrol_lti');
91 $regendpoint = $openidconfig->registration_endpoint ??
null;
92 if (empty($regendpoint)) {
93 throw new coding_exception('Missing registration endpoint in OpenID configuration');
96 // Build the client registration request to send to the platform.
97 $wwwrooturl = $CFG->wwwroot
;
98 $parsed = parse_url($wwwrooturl);
99 $sitefullname = format_string(get_site()->fullname
);
101 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
102 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
103 'https://purl.imsglobal.org/spec/lti-ags/scope/score',
104 'https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly',
107 $regrequest = (object) [
108 'application_type' => 'web',
109 'grant_types' => ['client_credentials', 'implicit'],
110 'response_types' => ['id_token'],
111 'initiate_login_uri' => $CFG->wwwroot
. '/enrol/lti/login.php?id=' . $draftreg->get_uniqueid(),
113 $CFG->wwwroot
. '/enrol/lti/launch.php',
114 $CFG->wwwroot
. '/enrol/lti/launch_deeplink.php',
116 // TODO: Consider whether to support client_name#ja syntax for multi language support - see MDL-73109.
117 'client_name' => format_string($SITE->fullname
, true, ['context' => system
::instance()]),
118 'jwks_uri' => $CFG->wwwroot
. '/enrol/lti/jwks.php',
119 'logo_uri' => $OUTPUT->get_compact_logo_url() ?
$OUTPUT->get_compact_logo_url()->out(false) : '',
120 'token_endpoint_auth_method' => 'private_key_jwt',
121 'scope' => implode(" ", $scopes),
122 'https://purl.imsglobal.org/spec/lti-tool-configuration' => [
123 'domain' => $parsed['host'],
124 'target_link_uri' => $CFG->wwwroot
. '/enrol/lti/launch.php',
125 'custom_parameters' => [],
137 'type' => 'LtiDeepLinkingRequest',
138 'allowLearner' => false,
139 'target_link_uri' => $CFG->wwwroot
. '/enrol/lti/launch_deeplink.php',
140 // TODO: Consider whether to support label#ja syntax for multi language support - see MDL-73109.
141 'label' => get_string('registrationdeeplinklabel', 'enrol_lti', $sitefullname),
147 'type' => 'LtiResourceLinkRequest',
148 'allowLearner' => true,
149 'target_link_uri' => $CFG->wwwroot
. '/enrol/lti/launch.php',
150 // TODO: Consider whether to support label#ja syntax for multi language support - see MDL-73109.
151 'label' => get_string('registrationresourcelinklabel', 'enrol_lti', $sitefullname),
160 $curl->setHeader(['Authorization: Bearer ' . $regtoken]);
161 $regrequest = json_encode($regrequest);
162 $regresponse = $curl->post($regendpoint, $regrequest);
163 $errno = $curl->get_errno();
165 throw new coding_exception("Error '{$errno}' while posting client registration request to client: {$regresponse}");
169 $regresponse = json_decode($regresponse);
170 if ($regresponse->client_id
) {
171 $toolconfig = $regresponse->{'https://purl.imsglobal.org/spec/lti-tool-configuration'};
173 if ($appregrepo->find_by_platform($openidconfig->issuer
, $regresponse->client_id
)) {
174 throw new moodle_exception('existingregistrationerror', 'enrol_lti');
177 // Registration of the tool on the platform was successful.
178 // Now update the platform details in the registration and mark it complete.
179 $draftreg->set_accesstokenurl(new moodle_url($openidconfig->token_endpoint
));
180 $draftreg->set_authenticationrequesturl(new moodle_url($openidconfig->authorization_endpoint
));
181 $draftreg->set_clientid($regresponse->client_id
);
182 $draftreg->set_jwksurl(new moodle_url($openidconfig->jwks_uri
));
183 $draftreg->set_platformid(new moodle_url($openidconfig->issuer
));
184 $draftreg->complete_registration();
185 $appreg = $appregrepo->save($draftreg);
187 $deployment = $appreg->add_tool_deployment($toolconfig->deployment_id
, $toolconfig->deployment_id
);
188 $deploymentrepo = new deployment_repository();
189 $deploymentrepo->save($deployment);
194 (window.opener || window.parent).postMessage({subject: 'org.imsglobal.lti.close'}, '$openidconfig->issuer');