Skip to content

Commit c3d4b5f

Browse files
committed
Log auth_oidc user out from all sessions when Front-channel logout URL is triggered
1 parent 61a5699 commit c3d4b5f

File tree

10 files changed

+158
-14
lines changed

10 files changed

+158
-14
lines changed

auth/oidc/classes/loginflow/authcode.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use moodle_exception;
3737
use moodle_url;
3838
use pix_icon;
39+
use stdClass;
3940

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

@@ -395,7 +396,11 @@ protected function handleauthresponse(array $authparams) {
395396
// Otherwise it's a user logging in normally with OIDC.
396397
$this->handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken);
397398
if ($USER->id && $DB->record_exists('auth_oidc_token', ['userid' => $USER->id])) {
398-
$DB->set_field('auth_oidc_token', 'sid', $sid, ['userid' => $USER->id]);
399+
$authoidsidrecord = new stdClass();
400+
$authoidsidrecord->userid = $USER->id;
401+
$authoidsidrecord->sid = $sid;
402+
$authoidsidrecord->timecreated = time();
403+
$DB->insert_record('auth_oidc_sid', $authoidsidrecord);
399404
}
400405
redirect(core_login_get_return_url());
401406
}
@@ -792,7 +797,7 @@ protected function handlelogin(string $oidcuniqid, array $authparams, array $tok
792797
$tokenrec = $DB->get_record('auth_oidc_token', ['id' => $tokenrec->id]);
793798
// This should be already done in auth_plugin_oidc::user_authenticated_hook, but just in case...
794799
if (!empty($tokenrec) && empty($tokenrec->userid)) {
795-
$updatedtokenrec = new \stdClass;
800+
$updatedtokenrec = new stdClass;
796801
$updatedtokenrec->id = $tokenrec->id;
797802
$updatedtokenrec->userid = $user->id;
798803
$DB->update_record('auth_oidc_token', $updatedtokenrec);

auth/oidc/classes/observers.php

+18-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525

2626
namespace auth_oidc;
2727

28+
use core\event\user_deleted;
29+
use core\event\user_loggedout;
30+
2831
defined('MOODLE_INTERNAL') || die();
2932

3033
require_once($CFG->dirroot.'/lib/filelib.php');
@@ -36,13 +39,26 @@ class observers {
3639
/**
3740
* Handle user_deleted event - clean up calendar subscriptions.
3841
*
39-
* @param \core\event\user_deleted $event The triggered event.
42+
* @param user_deleted $event The triggered event.
4043
* @return bool Success/Failure.
4144
*/
42-
public static function handle_user_deleted(\core\event\user_deleted $event) {
45+
public static function handle_user_deleted(user_deleted $event) {
4346
global $DB;
4447
$userid = $event->objectid;
4548
$DB->delete_records('auth_oidc_token', ['userid' => $userid]);
4649
return true;
4750
}
51+
52+
/**
53+
* Handle user_loggedout event - clean up sid records.
54+
*
55+
* @param user_loggedout $event The triggered event.
56+
* @return bool Success/Failure.
57+
*/
58+
public static function handle_user_loggedout(user_loggedout $event) {
59+
global $DB;
60+
$userid = $event->objectid;
61+
$DB->delete_records('auth_oidc_sid', ['userid' => $userid]);
62+
return true;
63+
}
4864
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
* A scheduled task to clean up oidc sid records.
19+
*
20+
* @package auth_oidc
21+
* @author Lai Wei <[email protected]>
22+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23+
* @copyright (C) 2021 onwards Microsoft, Inc. (http://microsoft.com/)
24+
*/
25+
26+
namespace auth_oidc\task;
27+
28+
use core\task\scheduled_task;
29+
30+
/**
31+
* A scheduled task that cleans up OIDC SID records.
32+
*/
33+
class cleanup_oidc_sid extends scheduled_task {
34+
/**
35+
* Get a descriptive name for the task.
36+
*/
37+
public function get_name() {
38+
return get_string('task_cleanup_oidc_sid', 'auth_oidc');
39+
}
40+
41+
/**
42+
* Clean up OIDC SID records.
43+
*/
44+
public function execute() {
45+
global $DB;
46+
47+
$DB->delete_records_select('auth_oidc_sid', 'timecreated < ?', [strtotime('-1 day')]);
48+
}
49+
}

auth/oidc/db/events.php

+6
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,10 @@
3232
'priority' => 200,
3333
'internal' => false,
3434
],
35+
[
36+
'eventname' => '\core\event\user_loggedout',
37+
'callback' => '\auth_oidc\observers::handle_user_loggedout',
38+
'priority' => 200,
39+
'internal' => false,
40+
],
3541
];

auth/oidc/db/install.xml

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" ?>
2-
<XMLDB PATH="auth/oidc/db" VERSION="20231221" COMMENT="XMLDB file for Moodle auth/oidc plugin"
2+
<XMLDB PATH="auth/oidc/db" VERSION="20241127" COMMENT="XMLDB file for Moodle auth/oidc plugin"
33
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
44
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
55
>
@@ -50,7 +50,6 @@
5050
<FIELD NAME="expiry" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="token expiry"/>
5151
<FIELD NAME="refreshtoken" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="refresh token"/>
5252
<FIELD NAME="idtoken" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="id token"/>
53-
<FIELD NAME="sid" TYPE="char" LENGTH="36" NOTNULL="false" SEQUENCE="false"/>
5453
</FIELDS>
5554
<KEYS>
5655
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
@@ -61,5 +60,16 @@
6160
<INDEX NAME="username" UNIQUE="false" FIELDS="username"/>
6261
</INDEXES>
6362
</TABLE>
63+
<TABLE NAME="auth_oidc_sid" COMMENT="Stores sid from IdP, to be used to find connected Moodle users when SLO is triggered from IdP.">
64+
<FIELDS>
65+
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
66+
<FIELD NAME="userid" TYPE="int" LENGTH="20" NOTNULL="true" SEQUENCE="false"/>
67+
<FIELD NAME="sid" TYPE="char" LENGTH="36" NOTNULL="true" SEQUENCE="false"/>
68+
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
69+
</FIELDS>
70+
<KEYS>
71+
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
72+
</KEYS>
73+
</TABLE>
6474
</TABLES>
6575
</XMLDB>

auth/oidc/db/tasks.php

+9
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,13 @@
3535
'dayofweek' => '*',
3636
'month' => '*',
3737
],
38+
[
39+
'classname' => 'auth_oidc\task\cleanup_oidc_sid',
40+
'blocking' => 0,
41+
'minute' => '51',
42+
'hour' => '*',
43+
'day' => '*',
44+
'dayofweek' => '*',
45+
'month' => '*',
46+
],
3847
];

auth/oidc/db/upgrade.php

+48-5
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function xmldb_auth_oidc_upgrade($oldversion) {
9494

9595
// Populate token oidcusername.
9696
if (empty($user->oidcusername)) {
97-
$updatedtoken = new \stdClass;
97+
$updatedtoken = new stdClass;
9898
$updatedtoken->id = $user->tokenid;
9999
$updatedtoken->oidcusername = $oidcusername;
100100
$DB->update_record('auth_oidc_token', $updatedtoken);
@@ -105,12 +105,12 @@ function xmldb_auth_oidc_upgrade($oldversion) {
105105
// Old username, update to upn/sub.
106106
if ($oidcusername != $user->username) {
107107
// Update username.
108-
$updateduser = new \stdClass;
108+
$updateduser = new stdClass;
109109
$updateduser->id = $user->userid;
110110
$updateduser->username = $oidcusername;
111111
$DB->update_record('user', $updateduser);
112112

113-
$updatedtoken = new \stdClass;
113+
$updatedtoken = new stdClass;
114114
$updatedtoken->id = $user->tokenid;
115115
$updatedtoken->username = $oidcusername;
116116
$DB->update_record('auth_oidc_token', $updatedtoken);
@@ -144,7 +144,7 @@ function xmldb_auth_oidc_upgrade($oldversion) {
144144
foreach ($authtokensrs as $authtokenrec) {
145145
$newusername = trim(\core_text::strtolower($authtokenrec->username));
146146
if ($newusername !== $authtokenrec->username) {
147-
$updatedrec = new \stdClass;
147+
$updatedrec = new stdClass;
148148
$updatedrec->id = $authtokenrec->id;
149149
$updatedrec->username = $newusername;
150150
$DB->update_record('auth_oidc_token', $updatedrec);
@@ -181,7 +181,7 @@ function xmldb_auth_oidc_upgrade($oldversion) {
181181
JOIN {user} u ON u.username = tok.username';
182182
$records = $DB->get_recordset_sql($sql);
183183
foreach ($records as $record) {
184-
$newrec = new \stdClass;
184+
$newrec = new stdClass;
185185
$newrec->id = $record->id;
186186
$newrec->userid = $record->userid;
187187
$DB->update_record('auth_oidc_token', $newrec);
@@ -504,5 +504,48 @@ function xmldb_auth_oidc_upgrade($oldversion) {
504504
upgrade_plugin_savepoint(true, 2022112836, 'auth', 'oidc');
505505
}
506506

507+
if ($oldversion < 2022112842) {
508+
// Define table auth_oidc_sid to be created.
509+
$table = new xmldb_table('auth_oidc_sid');
510+
511+
// Adding fields to table auth_oidc_sid.
512+
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
513+
$table->add_field('userid', XMLDB_TYPE_INTEGER, '20', null, XMLDB_NOTNULL, null, null);
514+
$table->add_field('sid', XMLDB_TYPE_CHAR, '36', null, XMLDB_NOTNULL, null, null);
515+
$table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
516+
517+
// Adding keys to table auth_oidc_sid.
518+
$table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
519+
520+
// Conditionally launch create table for auth_oidc_sid.
521+
if (!$dbman->table_exists($table)) {
522+
$dbman->create_table($table);
523+
}
524+
525+
// Migrate existing sid values from auth_oidc_tokens to auth_oidc_sid.
526+
$tokenrecords = $DB->get_records('auth_oidc_token');
527+
foreach ($tokenrecords as $tokenrecord) {
528+
if ($tokenrecord->sid) {
529+
$sidrecord = new stdClass();
530+
$sidrecord->userid = $tokenrecord->userid;
531+
$sidrecord->sid = $tokenrecord->sid;
532+
$sidrecord->timecreated = time();
533+
$DB->insert_record('auth_oidc_sid', $sidrecord);
534+
}
535+
}
536+
537+
// Define field sid to be dropped from auth_oidc_token.
538+
$table = new xmldb_table('auth_oidc_token');
539+
$field = new xmldb_field('sid');
540+
541+
// Conditionally launch drop field sid.
542+
if ($dbman->field_exists($table, $field)) {
543+
$dbman->drop_field($table, $field);
544+
}
545+
546+
// Oidc savepoint reached.
547+
upgrade_plugin_savepoint(true, 2022112842, 'auth', 'oidc');
548+
}
549+
507550
return true;
508551
}

auth/oidc/lang/en/auth_oidc.php

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
$string['event_debug'] = 'Debug message';
208208

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

211212
$string['errorauthdisconnectemptypassword'] = 'Password cannot be empty';
212213
$string['errorauthdisconnectemptyusername'] = 'Username cannot be empty';

auth/oidc/logout.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
* @copyright (C) 2014 onwards Microsoft, Inc. (http://microsoft.com/)
2424
*/
2525

26+
use core\session\manager;
27+
2628
// phpcs:ignore moodle.Files.RequireLogin.Missing
2729
require_once(__DIR__ . '/../../config.php');
2830

@@ -32,15 +34,18 @@
3234
$sid = optional_param('sid', '', PARAM_TEXT);
3335

3436
if ($sid) {
35-
if ($authoidctokenrecord = $DB->get_record('auth_oidc_token', ['sid' => $sid])) {
36-
if ($authoidctokenrecord->userid == $USER->id) {
37+
if ($authoidcsidrecord = $DB->get_record('auth_oidc_sid', ['sid' => $sid])) {
38+
if ($authoidcsidrecord->userid == $USER->id) {
3739
$authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
3840
foreach ($authsequence as $authname) {
3941
$authplugin = get_auth_plugin($authname);
4042
$authplugin->logoutpage_hook();
4143
}
4244

4345
require_logout();
46+
47+
// Log the user out from all sessions.
48+
manager::destroy_user_sessions($authoidcsidrecord->userid);
4449
}
4550
}
4651
}

auth/oidc/version.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

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

28-
$plugin->version = 2022112840;
28+
$plugin->version = 2022112842;
2929
$plugin->requires = 2022112800;
3030
$plugin->release = '4.1.9';
3131
$plugin->component = 'auth_oidc';

0 commit comments

Comments
 (0)