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
25define('NO_DEBUG_DISPLAY', true);
26define('NO_MOODLE_COOKIES', true);
27
28use Firebase\JWT\JWT;
29
30require_once(__DIR__ . '/../../config.php');
31require_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
45if ($ok) {
46    $ok = !empty($clientassertion) && !empty($clientassertiontype) &&
47          !empty($granttype) && !empty($scope);
48}
49
50if ($ok) {
51    $ok = ($clientassertiontype === 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer') &&
52          ($granttype === 'client_credentials');
53    $error = 'unsupported_grant_type';
54}
55
56if ($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
67if ($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
83if ($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
93if ($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}
104EOD;
105} else {
106    $response->set_code(400);
107    $body = <<< EOD
108{
109  "error" : "{$error}"
110}
111EOD;
112}
113
114$response->set_body($body);
115
116$response->send();