in classes/loginflow/authcode.php [179:281]
protected function handleauthresponse(array $authparams) {
global $DB, $SESSION, $USER, $CFG;
if (!empty($authparams['error_description'])) {
utils::debug('Authorization error.', 'authcode::handleauthresponse', $authparams);
redirect($CFG->wwwroot, get_string('errorauthgeneral', 'auth_oidc'), null, \core\output\notification::NOTIFY_ERROR);
}
if (!isset($authparams['code'])) {
utils::debug('No auth code received.', 'authcode::handleauthresponse', $authparams);
throw new \moodle_exception('errorauthnoauthcode', 'auth_oidc');
}
if (!isset($authparams['state'])) {
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 = [];
}
}
$SESSION->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.
[$oidcuniqid, $idtoken] = $this->process_idtoken($tokenparams['id_token'], $orignonce);
// Check restrictions.
$passed = $this->checkrestrictions($idtoken);
if ($passed !== true && empty($additionaldata['ignorerestrictions'])) {
$errstr = 'User prevented from logging in due to restrictions.';
utils::debug($errstr, 'handleauthresponse', $idtoken);
throw new \moodle_exception('errorrestricted', 'auth_oidc');
}
// This is for setting the system API user.
if (isset($additionaldata['justauth']) && $additionaldata['justauth'] === true) {
$eventdata = [
'other' => [
'authparams' => $authparams,
'tokenparams' => $tokenparams,
'statedata' => $additionaldata,
]
];
$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() && !isguestuser() && (empty($tokenrec) || (isset($USER->auth) && $USER->auth !== 'oidc'))) {
// If user is already logged in and trying to link Microsoft 365 account or use it for OIDC.
// Check if that Microsoft 365 account already exists in moodle.
$userrec = $DB->count_records_sql('SELECT COUNT(*)
FROM {user}
WHERE username = ?
AND id != ?',
[$idtoken->claim('upn'), $USER->id]);
if (!empty($userrec)) {
if (empty($additionaldata['redirect'])) {
$redirect = '/auth/oidc/ucp.php?o365accountconnected=true';
} else if ($additionaldata['redirect'] == '/local/o365/ucp.php') {
$redirect = $additionaldata['redirect'].'?action=connection&o365accountconnected=true';
} else {
throw new \moodle_exception('errorinvalidredirect_message', 'auth_oidc');
}
redirect(new \moodle_url($redirect));
}
// If the user is already logged in we can treat this as a "migration" - a user switching to OIDC.
$connectiononly = false;
if (isset($additionaldata['connectiononly']) && $additionaldata['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());
}
}