in classes/loginflow/authcode.php [425:548]
protected function handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken) {
global $DB, $CFG;
$tokenrec = $DB->get_record('auth_oidc_token', ['oidcuniqid' => $oidcuniqid]);
// Do not continue if auth plugin is not enabled.
if (!is_enabled_auth('oidc')) {
throw new \moodle_exception('erroroidcnotenabled', 'auth_oidc', null, null, '1');
}
if (!empty($tokenrec)) {
// Already connected user.
if (empty($tokenrec->userid)) {
// Existing token record, but missing the user ID.
$user = $DB->get_record('user', ['username' => $tokenrec->username]);
if (empty($user)) {
// Token exists, but it doesn't have a valid username.
// In this case, delete the token, and try to process login again.
$DB->delete_records('auth_oidc_token', ['id' => $tokenrec->id]);
return $this->handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken);
}
$tokenrec->userid = $user->id;
$DB->update_record('auth_oidc_token', $tokenrec);
} else {
// Existing token with a user ID.
$user = $DB->get_record('user', ['id' => $tokenrec->userid]);
if (empty($user)) {
$failurereason = AUTH_LOGIN_NOUSER;
$eventdata = ['other' => ['username' => $tokenrec->username, 'reason' => $failurereason]];
$event = \core\event\user_login_failed::create($eventdata);
$event->trigger();
// Token is invalid, delete it.
$DB->delete_records('auth_oidc_token', ['id' => $tokenrec->id]);
return $this->handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken);
}
}
$username = $user->username;
$this->updatetoken($tokenrec->id, $authparams, $tokenparams);
$user = authenticate_user_login($username, null, true);
if (!empty($user)) {
complete_user_login($user);
} else {
// There was a problem in authenticate_user_login.
throw new \moodle_exception('errorauthgeneral', 'auth_oidc', null, null, '2');
}
return true;
} else {
/* No existing token, user not connected. Possibilities:
- Matched user.
- New user (maybe create).
*/
// Generate a Moodle username.
// Use 'upn' if available for username (Azure-specific), or fall back to lower-case oidcuniqid.
$username = $idtoken->claim('upn');
$originalupn = null;
if (empty($username)) {
$username = $oidcuniqid;
// If upn claim is missing, it can mean either the IdP is not Azure AD, or it's a guest user.
if (\auth_oidc_is_local_365_installed()) {
$apiclient = \local_o365\utils::get_api();
$userdetails = $apiclient->get_user($oidcuniqid, true);
if (!is_null($userdetails) && isset($userdetails['userPrincipalName']) &&
stripos($userdetails['userPrincipalName'], '#EXT#') !== false && $idtoken->claim('unique_name')) {
$originalupn = $userdetails['userPrincipalName'];
$username = $idtoken->claim('unique_name');
}
}
}
// See if we have an object listing.
$username = $this->check_objects($oidcuniqid, $username);
$matchedwith = $this->check_for_matched($username);
if (!empty($matchedwith)) {
if ($matchedwith->auth != 'oidc') {
$matchedwith->aadupn = $username;
throw new \moodle_exception('errorusermatched', 'auth_oidc', null, $matchedwith);
}
}
$username = trim(\core_text::strtolower($username));
$tokenrec = $this->createtoken($oidcuniqid, $username, $authparams, $tokenparams, $idtoken, 0, $originalupn);
$existinguserparams = ['username' => $username, 'mnethostid' => $CFG->mnet_localhost_id];
if ($DB->record_exists('user', $existinguserparams) !== true) {
// User does not exist. Create user if site allows, otherwise fail.
if (empty($CFG->authpreventaccountcreation)) {
$user = create_user_record($username, null, 'oidc');
} else {
// Trigger login failed event.
$failurereason = AUTH_LOGIN_NOUSER;
$eventdata = ['other' => ['username' => $username, 'reason' => $failurereason]];
$event = \core\event\user_login_failed::create($eventdata);
$event->trigger();
throw new \moodle_exception('errorauthloginfailednouser', 'auth_oidc', null, null, '1');
}
}
$user = authenticate_user_login($username, null, true);
if (!empty($user)) {
$tokenrec = $DB->get_record('auth_oidc_token', ['id' => $tokenrec->id]);
// This should be already done in auth_plugin_oidc::user_authenticated_hook, but just in case...
if (!empty($tokenrec) && empty($tokenrec->userid)) {
$updatedtokenrec = new \stdClass;
$updatedtokenrec->id = $tokenrec->id;
$updatedtokenrec->userid = $user->id;
$DB->update_record('auth_oidc_token', $updatedtokenrec);
}
complete_user_login($user);
} else {
// There was a problem in authenticate_user_login. Clean up incomplete token record.
if (!empty($tokenrec)) {
$DB->delete_records('auth_oidc_token', ['id' => $tokenrec->id]);
}
redirect($CFG->wwwroot, get_string('errorauthgeneral', 'auth_oidc'), null, \core\output\notification::NOTIFY_ERROR);
}
return true;
}
}