Beispiel #1
0
 /**
  * Process an OIDC JSON response.
  *
  * @param string $response The received JSON.
  * @return array The parsed JSON.
  */
 public static function process_json_response($response, array $expectedstructure = array())
 {
     $backtrace = debug_backtrace(0);
     $callingclass = isset($backtrace[1]['class']) ? $backtrace[1]['class'] : '?';
     $callingfunc = isset($backtrace[1]['function']) ? $backtrace[1]['function'] : '?';
     $callingline = isset($backtrace[0]['line']) ? $backtrace[0]['line'] : '?';
     $caller = $callingclass . '::' . $callingfunc . ':' . $callingline;
     $result = @json_decode($response, true);
     if (empty($result) || !is_array($result)) {
         \auth_oidc\utils::debug('Bad response received', $caller, $response);
         throw new \moodle_exception('erroroidccall', 'auth_oidc');
     }
     if (isset($result['error'])) {
         $errmsg = 'Error response received.';
         \auth_oidc\utils::debug($errmsg, $caller, $result);
         if (isset($result['error_description'])) {
             throw new \moodle_exception('erroroidccall_message', 'auth_oidc', '', $result['error_description']);
         } else {
             throw new \moodle_exception('erroroidccall', 'auth_oidc');
         }
     }
     foreach ($expectedstructure as $key => $val) {
         if (!isset($result[$key])) {
             $errmsg = 'Invalid structure received. No "' . $key . '"';
             \auth_oidc\utils::debug($errmsg, $caller, $result);
             throw new \moodle_exception('erroroidccall', 'auth_oidc');
         }
         if ($val !== null && $result[$key] !== $val) {
             $strreceivedval = \auth_oidc\utils::tostring($result[$key]);
             $strval = \auth_oidc\utils::tostring($val);
             $errmsg = 'Invalid structure received. Invalid "' . $key . '". Received "' . $strreceivedval . '", expected "' . $strval . '"';
             \auth_oidc\utils::debug($errmsg, $caller, $result);
             throw new \moodle_exception('erroroidccall', 'auth_oidc');
         }
     }
     return $result;
 }
Beispiel #2
0
 /**
  * Handle an authorization request response received from the configured OP.
  *
  * @param array $authparams Received parameters.
  */
 protected function handleauthresponse(array $authparams)
 {
     global $DB, $CFG, $SESSION, $STATEADDITIONALDATA, $USER;
     if (!isset($authparams['code'])) {
         \auth_oidc\utils::debug('No auth code received.', 'authcode::handleauthresponse', $authparams);
         throw new \moodle_exception('errorauthnoauthcode', 'auth_oidc');
     }
     if (!isset($authparams['state'])) {
         \auth_oidc\utils::debug('No state received.', 'authcode::handleauthresponse', $authparams);
         throw new \moodle_exception('errorauthunknownstate', 'auth_oidc');
     }
     // Validate and expire state.
     $staterec = $DB->get_record('auth_oidc_state', ['state' => $authparams['state']]);
     if (empty($staterec)) {
         throw new \moodle_exception('errorauthunknownstate', 'auth_oidc');
     }
     $orignonce = $staterec->nonce;
     $additionaldata = [];
     if (!empty($staterec->additionaldata)) {
         $additionaldata = @unserialize($staterec->additionaldata);
         if (!is_array($additionaldata)) {
             $additionaldata = [];
         }
     }
     $STATEADDITIONALDATA = $additionaldata;
     $DB->delete_records('auth_oidc_state', ['id' => $staterec->id]);
     // Get token from auth code.
     $client = $this->get_oidcclient();
     $tokenparams = $client->tokenrequest($authparams['code']);
     if (!isset($tokenparams['id_token'])) {
         throw new \moodle_exception('errorauthnoidtoken', 'auth_oidc');
     }
     // Decode and verify idtoken.
     list($oidcuniqid, $idtoken) = $this->process_idtoken($tokenparams['id_token'], $orignonce);
     // Check restrictions.
     $passed = $this->checkrestrictions($idtoken);
     if ($passed !== true) {
         $errstr = 'User prevented from logging in due to restrictions.';
         \auth_oidc\utils::debug($errstr, 'handleauthresponse', $idtoken);
         throw new \moodle_exception('errorrestricted', 'auth_oidc');
     }
     // This is for setting the system API user.
     if (isset($SESSION->auth_oidc_justevent)) {
         unset($SESSION->auth_oidc_justevent);
         $eventdata = ['other' => ['authparams' => $authparams, 'tokenparams' => $tokenparams]];
         $event = \auth_oidc\event\user_authed::create($eventdata);
         $event->trigger();
         return true;
     }
     // Check if OIDC user is already migrated.
     $tokenrec = $DB->get_record('auth_oidc_token', ['oidcuniqid' => $oidcuniqid]);
     if (isloggedin() === true && (empty($tokenrec) || isset($USER->auth) && $USER->auth !== 'oidc')) {
         // If the user is already logged in we can treat this as a "migration" - a user switching to OIDC.
         $connectiononly = false;
         if (isset($SESSION->auth_oidc_connectiononly)) {
             $connectiononly = true;
             unset($SESSION->auth_oidc_connectiononly);
         }
         if (isset($STATEADDITIONALDATA['connectiononly']) && $STATEADDITIONALDATA['connectiononly'] === true) {
             $connectiononly = true;
         }
         $this->handlemigration($oidcuniqid, $authparams, $tokenparams, $idtoken, $connectiononly);
         $redirect = !empty($additionaldata['redirect']) ? $additionaldata['redirect'] : '/auth/oidc/ucp.php';
         redirect(new \moodle_url($redirect));
     } else {
         // Otherwise it's a user logging in normally with OIDC.
         $this->handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken);
         redirect(core_login_get_return_url());
     }
 }
 /**
  * Exchange an authorization code for an access token.
  *
  * @param string $tokenendpoint The token endpoint URI.
  * @param string $code An authorization code.
  * @return array Received parameters.
  */
 public function tokenrequest($code)
 {
     if (empty($this->endpoints['token'])) {
         throw new \moodle_exception('erroroidcclientnotokenendpoint', 'auth_oidc');
     }
     $params = ['client_id' => $this->clientid, 'client_secret' => $this->clientsecret, 'grant_type' => 'authorization_code', 'code' => $code, 'redirect_uri' => $this->redirecturi];
     $returned = $this->httpclient->post($this->endpoints['token'], $params);
     return \auth_oidc\utils::process_json_response($returned, ['id_token' => null]);
 }
Beispiel #4
0
 /**
  * Check user restrictions, if present.
  *
  * This check will return false if there are restrictions in place that the user did not meet, otherwise it will return
  * true. If there are no restrictions in place, this will return true.
  *
  * @param \auth_oidc\jwt $idtoken The ID token of the user who is trying to log in.
  * @return bool Whether the restriction check passed.
  */
 protected function checkrestrictions(\auth_oidc\jwt $idtoken)
 {
     $restrictions = isset($this->config->userrestrictions) ? trim($this->config->userrestrictions) : '';
     $hasrestrictions = false;
     $userpassed = false;
     if ($restrictions !== '') {
         $restrictions = explode("\n", $restrictions);
         // Match "UPN" (Azure-specific) if available, otherwise match oidc-standard "sub".
         $tomatch = $idtoken->claim('upn');
         if (empty($tomatch)) {
             $tomatch = $idtoken->claim('sub');
         }
         foreach ($restrictions as $restriction) {
             $restriction = trim($restriction);
             if ($restriction !== '') {
                 $hasrestrictions = true;
                 ob_start();
                 try {
                     $count = @preg_match('/' . $restriction . '/', $tomatch, $matches);
                     if (!empty($count)) {
                         $userpassed = true;
                         break;
                     }
                 } catch (\Exception $e) {
                     $debugdata = ['exception' => $e, 'restriction' => $restriction, 'tomatch' => $tomatch];
                     \auth_oidc\utils::debug('Error running user restrictions.', 'handleauthresponse', $debugdata);
                 }
                 $contents = ob_get_contents();
                 ob_end_clean();
                 if (!empty($contents)) {
                     $debugdata = ['contents' => $contents, 'restriction' => $restriction, 'tomatch' => $tomatch];
                     \auth_oidc\utils::debug('Output while running user restrictions.', 'handleauthresponse', $debugdata);
                 }
             }
         }
     }
     return $hasrestrictions === true && $userpassed !== true ? false : true;
 }
Beispiel #5
0
 /**
  * This is the primary method that is used by the authenticate_user_login() function in moodlelib.php.
  *
  * @param string $username The username (with system magic quotes)
  * @param string $password The password (with system magic quotes)
  * @return bool Authentication success or failure.
  */
 public function user_login($username, $password = null)
 {
     global $CFG, $DB;
     $client = $this->get_oidcclient();
     $authparams = ['code' => ''];
     $oidcusername = $username;
     $oidctoken = $DB->get_records('auth_oidc_token', ['username' => $username]);
     if (!empty($oidctoken)) {
         $oidctoken = array_shift($oidctoken);
         if (!empty($oidctoken) && !empty($oidctoken->oidcusername)) {
             $oidcusername = $oidctoken->oidcusername;
         }
     }
     // Make request.
     $tokenparams = $client->rocredsrequest($oidcusername, $password);
     if (!empty($tokenparams) && isset($tokenparams['token_type']) && $tokenparams['token_type'] === 'Bearer') {
         list($oidcuniqid, $idtoken) = $this->process_idtoken($tokenparams['id_token']);
         // Check restrictions.
         $passed = $this->checkrestrictions($idtoken);
         if ($passed !== true) {
             $errstr = 'User prevented from logging in due to restrictions.';
             \auth_oidc\utils::debug($errstr, 'handleauthresponse', $idtoken);
             return false;
         }
         $tokenrec = $DB->get_record('auth_oidc_token', ['oidcuniqid' => $oidcuniqid]);
         if (!empty($tokenrec)) {
             $this->updatetoken($tokenrec->id, $authparams, $tokenparams);
         } else {
             $tokenrec = $this->createtoken($oidcuniqid, $username, $authparams, $tokenparams, $idtoken);
         }
         return true;
     }
     return false;
 }