Skip to content

Commit

Permalink
Log auth_oidc user out from all sessions when Front-channel logout UR…
Browse files Browse the repository at this point in the history
…L is triggered
  • Loading branch information
weilai-irl committed Nov 29, 2024
1 parent d65c9fc commit 8dfadc4
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 14 deletions.
9 changes: 7 additions & 2 deletions auth/oidc/classes/loginflow/authcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use moodle_exception;
use moodle_url;
use pix_icon;
use stdClass;

defined('MOODLE_INTERNAL') || die();

Expand Down Expand Up @@ -395,7 +396,11 @@ protected function handleauthresponse(array $authparams) {
// Otherwise it's a user logging in normally with OIDC.
$this->handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken);
if ($USER->id && $DB->record_exists('auth_oidc_token', ['userid' => $USER->id])) {
$DB->set_field('auth_oidc_token', 'sid', $sid, ['userid' => $USER->id]);
$authoidsidrecord = new stdClass();
$authoidsidrecord->userid = $USER->id;
$authoidsidrecord->sid = $sid;
$authoidsidrecord->timecreated = time();
$DB->insert_record('auth_oidc_sid', $authoidsidrecord);
}
redirect(core_login_get_return_url());
}
Expand Down Expand Up @@ -792,7 +797,7 @@ protected function handlelogin(string $oidcuniqid, array $authparams, array $tok
$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 = new stdClass;
$updatedtokenrec->id = $tokenrec->id;
$updatedtokenrec->userid = $user->id;
$DB->update_record('auth_oidc_token', $updatedtokenrec);
Expand Down
20 changes: 18 additions & 2 deletions auth/oidc/classes/observers.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@

namespace auth_oidc;

use core\event\user_deleted;
use core\event\user_loggedout;

defined('MOODLE_INTERNAL') || die();

require_once($CFG->dirroot.'/lib/filelib.php');
Expand All @@ -36,13 +39,26 @@ class observers {
/**
* Handle user_deleted event - clean up calendar subscriptions.
*
* @param \core\event\user_deleted $event The triggered event.
* @param user_deleted $event The triggered event.
* @return bool Success/Failure.
*/
public static function handle_user_deleted(\core\event\user_deleted $event) {
public static function handle_user_deleted(user_deleted $event) {
global $DB;
$userid = $event->objectid;
$DB->delete_records('auth_oidc_token', ['userid' => $userid]);
return true;
}

/**
* Handle user_loggedout event - clean up sid records.
*
* @param user_loggedout $event The triggered event.
* @return bool Success/Failure.
*/
public static function handle_user_loggedout(user_loggedout $event) {
global $DB;
$userid = $event->objectid;
$DB->delete_records('auth_oidc_sid', ['userid' => $userid]);
return true;
}
}
49 changes: 49 additions & 0 deletions auth/oidc/classes/task/cleanup_oidc_sid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* A scheduled task to clean up oidc sid records.
*
* @package auth_oidc
* @author Lai Wei <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright (C) 2021 onwards Microsoft, Inc. (http://microsoft.com/)
*/

namespace auth_oidc\task;

use core\task\scheduled_task;

/**
* A scheduled task that cleans up OIDC SID records.
*/
class cleanup_oidc_sid extends scheduled_task {
/**
* Get a descriptive name for the task.
*/
public function get_name() {
return get_string('task_cleanup_oidc_sid', 'auth_oidc');
}

/**
* Clean up OIDC SID records.
*/
public function execute() {
global $DB;

$DB->delete_records_select('auth_oidc_sid', 'timecreated < ?', [strtotime('-1 day')]);
}
}
6 changes: 6 additions & 0 deletions auth/oidc/db/events.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@
'priority' => 200,
'internal' => false,
],
[
'eventname' => '\core\event\user_loggedout',
'callback' => '\auth_oidc\observers::handle_user_loggedout',
'priority' => 200,
'internal' => false,
],
];
14 changes: 12 additions & 2 deletions auth/oidc/db/install.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="auth/oidc/db" VERSION="20231221" COMMENT="XMLDB file for Moodle auth/oidc plugin"
<XMLDB PATH="auth/oidc/db" VERSION="20241127" COMMENT="XMLDB file for Moodle auth/oidc plugin"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
Expand Down Expand Up @@ -50,7 +50,6 @@
<FIELD NAME="expiry" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="token expiry"/>
<FIELD NAME="refreshtoken" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="refresh token"/>
<FIELD NAME="idtoken" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="id token"/>
<FIELD NAME="sid" TYPE="char" LENGTH="36" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
Expand All @@ -61,5 +60,16 @@
<INDEX NAME="username" UNIQUE="false" FIELDS="username"/>
</INDEXES>
</TABLE>
<TABLE NAME="auth_oidc_sid" COMMENT="Stores sid from IdP, to be used to find connected Moodle users when SLO is triggered from IdP.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="userid" TYPE="int" LENGTH="20" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="sid" TYPE="char" LENGTH="36" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
9 changes: 9 additions & 0 deletions auth/oidc/db/tasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,13 @@
'dayofweek' => '*',
'month' => '*',
],
[
'classname' => 'auth_oidc\task\cleanup_oidc_sid',
'blocking' => 0,
'minute' => '51',
'hour' => '*',
'day' => '*',
'dayofweek' => '*',
'month' => '*',
],
];
53 changes: 48 additions & 5 deletions auth/oidc/db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function xmldb_auth_oidc_upgrade($oldversion) {

// Populate token oidcusername.
if (empty($user->oidcusername)) {
$updatedtoken = new \stdClass;
$updatedtoken = new stdClass;
$updatedtoken->id = $user->tokenid;
$updatedtoken->oidcusername = $oidcusername;
$DB->update_record('auth_oidc_token', $updatedtoken);
Expand All @@ -105,12 +105,12 @@ function xmldb_auth_oidc_upgrade($oldversion) {
// Old username, update to upn/sub.
if ($oidcusername != $user->username) {
// Update username.
$updateduser = new \stdClass;
$updateduser = new stdClass;
$updateduser->id = $user->userid;
$updateduser->username = $oidcusername;
$DB->update_record('user', $updateduser);

$updatedtoken = new \stdClass;
$updatedtoken = new stdClass;
$updatedtoken->id = $user->tokenid;
$updatedtoken->username = $oidcusername;
$DB->update_record('auth_oidc_token', $updatedtoken);
Expand Down Expand Up @@ -144,7 +144,7 @@ function xmldb_auth_oidc_upgrade($oldversion) {
foreach ($authtokensrs as $authtokenrec) {
$newusername = trim(\core_text::strtolower($authtokenrec->username));
if ($newusername !== $authtokenrec->username) {
$updatedrec = new \stdClass;
$updatedrec = new stdClass;
$updatedrec->id = $authtokenrec->id;
$updatedrec->username = $newusername;
$DB->update_record('auth_oidc_token', $updatedrec);
Expand Down Expand Up @@ -181,7 +181,7 @@ function xmldb_auth_oidc_upgrade($oldversion) {
JOIN {user} u ON u.username = tok.username';
$records = $DB->get_recordset_sql($sql);
foreach ($records as $record) {
$newrec = new \stdClass;
$newrec = new stdClass;
$newrec->id = $record->id;
$newrec->userid = $record->userid;
$DB->update_record('auth_oidc_token', $newrec);
Expand Down Expand Up @@ -504,5 +504,48 @@ function xmldb_auth_oidc_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2024042201, 'auth', 'oidc');
}

if ($oldversion < 2024100702) {
// Define table auth_oidc_sid to be created.
$table = new xmldb_table('auth_oidc_sid');

// Adding fields to table auth_oidc_sid.
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('userid', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, null);
$table->add_field('sid', XMLDB_TYPE_CHAR, '36', null, XMLDB_NOTNULL, null, null);
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);

// Adding keys to table auth_oidc_sid.
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);

// Conditionally launch create table for auth_oidc_sid.
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}

// Migrate existing sid values from auth_oidc_tokens to auth_oidc_sid.
$tokenrecords = $DB->get_records('auth_oidc_token');
foreach ($tokenrecords as $tokenrecord) {
if ($tokenrecord->sid) {
$sidrecord = new stdClass();
$sidrecord->userid = $tokenrecord->userid;
$sidrecord->sid = $tokenrecord->sid;
$sidrecord->timecreated = time();
$DB->insert_record('auth_oidc_sid', $sidrecord);
}
}

// Define field sid to be dropped from auth_oidc_token.
$table = new xmldb_table('auth_oidc_token');
$field = new xmldb_field('sid');

// Conditionally launch drop field sid.
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}

// Oidc savepoint reached.
upgrade_plugin_savepoint(true, 2024100702, 'auth', 'oidc');
}

return true;
}
1 change: 1 addition & 0 deletions auth/oidc/lang/en/auth_oidc.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@
$string['event_debug'] = 'Debug message';

$string['task_cleanup_oidc_state_and_token'] = 'Clean up OIDC state and invalid token';
$string['task_cleanup_oidc_sid'] = 'Clean up OIDC SID records';

$string['errorauthdisconnectemptypassword'] = 'Password cannot be empty';
$string['errorauthdisconnectemptyusername'] = 'Username cannot be empty';
Expand Down
10 changes: 8 additions & 2 deletions auth/oidc/logout.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
*/

// phpcs:ignore moodle.Files.RequireLogin.Missing

use core\session\manager;

require_once(__DIR__ . '/../../config.php');

$PAGE->set_url('/auth/oidc/logout.php');
Expand All @@ -32,15 +35,18 @@
$sid = optional_param('sid', '', PARAM_TEXT);

if ($sid) {
if ($authoidctokenrecord = $DB->get_record('auth_oidc_token', ['sid' => $sid])) {
if ($authoidctokenrecord->userid == $USER->id) {
if ($authoidcsidrecord = $DB->get_record('auth_oidc_sid', ['sid' => $sid])) {
if ($authoidcsidrecord->userid == $USER->id) {
$authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
foreach ($authsequence as $authname) {
$authplugin = get_auth_plugin($authname);
$authplugin->logoutpage_hook();
}

require_logout();

// Log the user out from all sessions.
manager::destroy_user_sessions($authoidcsidrecord->userid);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion auth/oidc/version.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2024100700;
$plugin->version = 2024100702;
$plugin->requires = 2024100700;
$plugin->release = '4.5.0';
$plugin->component = 'auth_oidc';
Expand Down

0 comments on commit 8dfadc4

Please sign in to comment.