Shield Security for WordPress - Version 15.0.5

Version Description

Download this release

Release Info

Developer paultgoodchild
Plugin Icon 128x128 Shield Security for WordPress
Version 15.0.5
Comparing to
See all releases

Code changes from version 15.0.4 to 15.0.5

cl.json CHANGED
@@ -160,6 +160,20 @@
160
  "type": "fixed"
161
  }
162
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
  ]
165
  },
160
  "type": "fixed"
161
  }
162
  ]
163
+ },
164
+ {
165
+ "version": "5",
166
+ "released_at": 1652128056,
167
+ "items": [
168
+ {
169
+ "title": "Prevent a warning being displayed during WP login.",
170
+ "type": "fixed"
171
+ },
172
+ {
173
+ "title": "Prevent a reported fatal error.",
174
+ "type": "fixed"
175
+ }
176
+ ]
177
  }
178
  ]
179
  },
icwp-wpsf.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://shsec.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
- * Version: 15.0.4
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
  * Author: Shield Security
3
  * Plugin Name: Shield Security
4
  * Plugin URI: https://shsec.io/2f
5
  * Description: Powerful, Easy-To-Use #1 Rated WordPress Security System
6
+ * Version: 15.0.5
7
  * Text Domain: wp-simple-firewall
8
  * Domain Path: /languages
9
  * Author: Shield Security
init.php CHANGED
@@ -65,6 +65,14 @@ catch ( Shield\Controller\Exceptions\VersionMismatchException $e ) {
65
  'Shield Security: There appears to be a configuration issue - please reinstall the Shield Security plugin.' );
66
  } );
67
  }
 
 
 
 
 
 
 
 
68
  catch ( \Exception $e ) {
69
  error_log( 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.' );
70
  error_log( $e->getMessage() );
65
  'Shield Security: There appears to be a configuration issue - please reinstall the Shield Security plugin.' );
66
  } );
67
  }
68
+ catch ( Shield\Controller\Exceptions\PluginConfigInvalidException $e ) {
69
+ add_action( 'admin_notices', function () use ( $e ) {
70
+ echo sprintf( '<div class="notice error"><p>%s</p><p>%s</p></div>',
71
+ 'Shield Security: Could not load the plugin modules configuration. Please refresh and if the problem persists, please reinstall the Shield plugin.',
72
+ $e->getMessage()
73
+ );
74
+ } );
75
+ }
76
  catch ( \Exception $e ) {
77
  error_log( 'Perhaps due to a failed upgrade, the Shield plugin failed to load certain component(s) - you should remove the plugin and reinstall.' );
78
  error_log( $e->getMessage() );
plugin-spec.php CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "15.0.4",
4
- "release_timestamp": 1652107639,
5
- "build": "202205.0904",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
1
  {
2
  "properties": {
3
+ "version": "15.0.5",
4
+ "release_timestamp": 1652128056,
5
+ "build": "202205.0905",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
plugin.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "properties": {
3
- "version": "15.0.4",
4
- "release_timestamp": 1652107639,
5
- "build": "202205.0904",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
1
  {
2
  "properties": {
3
+ "version": "15.0.5",
4
+ "release_timestamp": 1652128056,
5
+ "build": "202205.0905",
6
  "slug_parent": "icwp",
7
  "slug_plugin": "wpsf",
8
  "human_name": "Shield Security",
readme.txt CHANGED
@@ -8,7 +8,7 @@ Requires at least: 3.7
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 6.0
11
- Stable tag: 15.0.4
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
8
  Requires PHP: 7.0
9
  Recommended PHP: 7.4
10
  Tested up to: 6.0
11
+ Stable tag: 15.0.5
12
 
13
  No-Nonsense Security Hardening that protects WordPress against hackers, malicious bots, and spammers (no captchas!). Now with exclusive ShieldNET Technology.
14
 
src/lib/src/Controller/Config/Ops/Save.php CHANGED
@@ -5,6 +5,9 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Config\Ops;
5
  use FernleafSystems\Wordpress\Plugin\Shield\Controller\Config\ConfigVO;
6
  use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
7
 
 
 
 
8
  class Save {
9
 
10
  public static function ToWp( ConfigVO $cfg, string $key ) :bool {
5
  use FernleafSystems\Wordpress\Plugin\Shield\Controller\Config\ConfigVO;
6
  use FernleafSystems\Wordpress\Services\Utilities\Options\Transient;
7
 
8
+ /**
9
+ * @deprecated 15.0
10
+ */
11
  class Save {
12
 
13
  public static function ToWp( ConfigVO $cfg, string $key ) :bool {
src/lib/src/Controller/Controller.php CHANGED
@@ -4,6 +4,7 @@ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
 
7
  use FernleafSystems\Wordpress\Plugin\Shield\Controller\Plugin\PluginDeactivate;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Config\LoadConfig;
9
  use FernleafSystems\Wordpress\Services\Services;
@@ -894,9 +895,60 @@ class Controller extends DynPropertiesClass {
894
  ->setCon( $this )
895
  ->run();
896
  }
 
 
 
 
 
897
  return $this->cfg;
898
  }
899
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
900
  /**
901
  * @return string|null
902
  */
@@ -1083,7 +1135,7 @@ class Controller extends DynPropertiesClass {
1083
  Transient::Delete( $this->getConfigStoreKey() );
1084
  }
1085
  elseif ( isset( $this->cfg ) ) {
1086
- Config\Ops\Save::ToWp( $this->cfg, $this->getConfigStoreKey() );
1087
  }
1088
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1089
  }
@@ -1177,26 +1229,6 @@ class Controller extends DynPropertiesClass {
1177
  * @throws \Exception
1178
  */
1179
  public function loadAllFeatures() :bool {
1180
- $modsCfg = empty( $this->cfg->mods_cfg ) ? [] : $this->cfg->mods_cfg;
1181
-
1182
- // First load all module Configs
1183
- foreach ( $this->cfg->modules as $slug ) {
1184
- $modsCfg[ $slug ] = ( new LoadConfig( $slug, $modsCfg[ $slug ] ?? null ) )
1185
- ->setCon( $this )
1186
- ->run();
1187
- }
1188
-
1189
- // Order Modules
1190
- uasort( $modsCfg, function ( $a, $b ) {
1191
- /** @var Shield\Modules\Base\Config\ModConfigVO $a */
1192
- /** @var Shield\Modules\Base\Config\ModConfigVO $b */
1193
- if ( $a->properties[ 'load_priority' ] == $b->properties[ 'load_priority' ] ) {
1194
- return 0;
1195
- }
1196
- return ( $a->properties[ 'load_priority' ] < $b->properties[ 'load_priority' ] ) ? -1 : 1;
1197
- } );
1198
-
1199
- $this->cfg->mods_cfg = $modsCfg;
1200
 
1201
  foreach ( $this->cfg->mods_cfg as $cfg ) {
1202
  try {
@@ -1239,11 +1271,13 @@ class Controller extends DynPropertiesClass {
1239
  $mod = $this->modules[ $slug ] ?? null;
1240
  if ( !$mod instanceof Shield\Modules\Base\ModCon ) {
1241
  try {
 
 
 
1242
  $mod = $this->loadFeatureHandler( $this->cfg->mods_cfg[ $slug ] );
1243
  }
1244
  catch ( \Exception $e ) {
1245
  error_log( $e->getMessage() );
1246
- die();
1247
  }
1248
  }
1249
  return $mod;
4
 
5
  use FernleafSystems\Utilities\Data\Adapter\DynPropertiesClass;
6
  use FernleafSystems\Wordpress\Plugin\Shield;
7
+ use FernleafSystems\Wordpress\Plugin\Shield\Controller\Exceptions;
8
  use FernleafSystems\Wordpress\Plugin\Shield\Controller\Plugin\PluginDeactivate;
9
  use FernleafSystems\Wordpress\Plugin\Shield\Modules\Base\Config\LoadConfig;
10
  use FernleafSystems\Wordpress\Services\Services;
895
  ->setCon( $this )
896
  ->run();
897
  }
898
+
899
+ $this->loadModConfigs();
900
+
901
+ $this->saveCurrentPluginControllerOptions();
902
+
903
  return $this->cfg;
904
  }
905
 
906
+ /**
907
+ * @throws Exceptions\PluginConfigInvalidException
908
+ */
909
+ private function loadModConfigs() {
910
+ if ( empty( $this->cfg->modules ) ) {
911
+ throw new Exceptions\PluginConfigInvalidException( 'No modules specified in the plugin config.' );
912
+ }
913
+
914
+ $modConfigs = empty( $this->cfg->mods_cfg ) ? [] : $this->cfg->mods_cfg;
915
+
916
+ // First load all module Configs
917
+ foreach ( $this->cfg->modules as $slug ) {
918
+ try {
919
+ $modCfg = ( new LoadConfig( $slug, $modConfigs[ $slug ] ?? null ) )
920
+ ->setCon( $this )
921
+ ->run();
922
+ }
923
+ catch ( \Exception $e ) {
924
+ throw new Exceptions\PluginConfigInvalidException( sprintf( "Exception loading config for module '%s': %s",
925
+ $slug, $e->getMessage() ) );
926
+ }
927
+
928
+ if ( empty( $modCfg ) || empty( $modCfg->properties ) ) {
929
+ throw new Exceptions\PluginConfigInvalidException( sprintf( "Loading config for module '%s' failed.", $slug ) );
930
+ }
931
+
932
+ $modConfigs[ $slug ] = $modCfg;
933
+ }
934
+
935
+ // Order Modules
936
+ uasort( $modConfigs, function ( $a, $b ) {
937
+ /** @var Shield\Modules\Base\Config\ModConfigVO $a */
938
+ /** @var Shield\Modules\Base\Config\ModConfigVO $b */
939
+ if ( $a->properties[ 'load_priority' ] == $b->properties[ 'load_priority' ] ) {
940
+ return 0;
941
+ }
942
+ return ( $a->properties[ 'load_priority' ] < $b->properties[ 'load_priority' ] ) ? -1 : 1;
943
+ } );
944
+
945
+ $this->cfg->mods_cfg = $modConfigs;
946
+ // Sanity checking: count to ensure that when we set the cfgs, they were correctly set.
947
+ if ( count( $this->cfg->getRawData()[ 'mods_cfg' ] ?? [] ) !== count( $modConfigs ) ) {
948
+ throw new Exceptions\PluginConfigInvalidException( "Building and storing module configurations failed." );
949
+ }
950
+ }
951
+
952
  /**
953
  * @return string|null
954
  */
1135
  Transient::Delete( $this->getConfigStoreKey() );
1136
  }
1137
  elseif ( isset( $this->cfg ) ) {
1138
+ Transient::Set( $this->getConfigStoreKey(), $this->cfg->getRawData() );
1139
  }
1140
  remove_filter( $this->prefix( 'bypass_is_plugin_admin' ), '__return_true' );
1141
  }
1229
  * @throws \Exception
1230
  */
1231
  public function loadAllFeatures() :bool {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
 
1233
  foreach ( $this->cfg->mods_cfg as $cfg ) {
1234
  try {
1271
  $mod = $this->modules[ $slug ] ?? null;
1272
  if ( !$mod instanceof Shield\Modules\Base\ModCon ) {
1273
  try {
1274
+ if ( empty( $this->cfg->mods_cfg[ $slug ] ) ) {
1275
+ throw new \Exception( 'No config available for module: '.$slug );
1276
+ }
1277
  $mod = $this->loadFeatureHandler( $this->cfg->mods_cfg[ $slug ] );
1278
  }
1279
  catch ( \Exception $e ) {
1280
  error_log( $e->getMessage() );
 
1281
  }
1282
  }
1283
  return $mod;
src/lib/src/Controller/Exceptions/PluginConfigInvalidException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php declare( strict_types=1 );
2
+
3
+ namespace FernleafSystems\Wordpress\Plugin\Shield\Controller\Exceptions;
4
+
5
+ class PluginConfigInvalidException extends \LogicException {
6
+
7
+ }
src/lib/src/Modules/Base/Options.php CHANGED
@@ -680,6 +680,9 @@ class Options {
680
  }
681
  }
682
 
 
 
 
683
  public function getConfigLoader() :Config\LoadConfig {
684
  if ( empty( $this->cfgLoader ) ) {
685
  $this->cfgLoader = new Config\LoadConfig( $this->getMod()->getSlug() );
680
  }
681
  }
682
 
683
+ /**
684
+ * @deprecated 15.0
685
+ */
686
  public function getConfigLoader() :Config\LoadConfig {
687
  if ( empty( $this->cfgLoader ) ) {
688
  $this->cfgLoader = new Config\LoadConfig( $this->getMod()->getSlug() );
src/lib/src/Modules/Sessions/Lib/SessionController.php CHANGED
@@ -71,44 +71,51 @@ class SessionController extends ExecOnceModConsumer {
71
  }
72
 
73
  if ( is_array( $parsed ) && !empty( $parsed[ 'token' ] ) ) {
74
- $manager = \WP_Session_Tokens::get_instance( $WPUsers->getCurrentWpUser()->ID );
75
 
76
- $session = $manager->get( $parsed[ 'token' ] );
77
-
78
- if ( is_array( $session ) ) {
79
-
80
- // Ensure the correct IP is stored
81
- $srvIP = Services::IP();
82
- $ip = $srvIP->getRequestIp();
83
- if ( !empty( $ip ) && ( empty( $session[ 'ip' ] ) || !$srvIP->checkIp( $ip, $session[ 'ip' ] ) ) ) {
84
- $session[ 'ip' ] = $ip;
85
- }
86
-
87
- $shieldSessionMeta = $session[ 'shield' ] ?? [];
88
-
89
- $shieldSessionMeta[ 'last_activity_at' ] = Services::Request()->ts();
90
- if ( empty( $shieldSessionMeta[ 'unique' ] ) ) {
91
- $shieldSessionMeta[ 'unique' ] = uniqid();
92
- }
93
-
94
- $session[ 'shield' ] = $shieldSessionMeta;
95
- $manager->update( $parsed[ 'token' ], $session );
96
-
97
- // all that follows should not be stored
98
- $session[ 'token' ] = $parsed[ 'token' ];
99
- // This is a copy of \WP_Session_Tokens::hash_token(). They made it private, cuz that's helpful.
100
- $session[ 'hashed_token' ] = function_exists( 'hash' ) ? hash( 'sha256', $parsed[ 'token' ] ) : sha1( $parsed[ 'token' ] );
101
- $session[ 'valid' ] = true;
102
- $this->currentWP->applyFromArray( $session );
103
-
104
- // Update User Last Seen IP.
105
- try {
106
- $this->getCon()->getCurrentUserMeta()->record->ip_ref = ( new IPRecords() )
107
- ->setMod( $this->getCon()->getModule_Data() )
108
- ->loadIP( $session[ 'ip' ], true )
109
- ->id;
110
- }
111
- catch ( \Exception $e ) {
 
 
 
 
 
 
 
 
112
  }
113
  }
114
  }
@@ -135,21 +142,25 @@ class SessionController extends ExecOnceModConsumer {
135
  }
136
 
137
  public function updateSessionParameter( string $key, $value ) {
138
- $WPUsers = Services::WpUsers();
139
  $current = $this->getCurrentWP();
140
  if ( $current->valid ) {
141
- $shield = $current->shield;
142
- $shield[ $key ] = $value;
143
- $current->shield = $shield;
144
- \WP_Session_Tokens::get_instance( $WPUsers->getCurrentWpUserId() )
145
- ->update(
146
- $current->token,
147
- array_diff_key( $current->getRawData(), array_flip( [
148
- 'token',
149
- 'hashed_token',
150
- 'valid'
151
- ] ) )
152
- );
 
 
 
 
 
153
  }
154
  }
155
 
@@ -165,13 +176,16 @@ class SessionController extends ExecOnceModConsumer {
165
 
166
  if ( $current->valid ) {
167
  $user = Services::WpUsers()->getCurrentWpUser();
168
- \WP_Session_Tokens::get_instance( $user->ID )->destroy( $current->token );
169
- $this->getCon()->fireEvent( 'session_terminate_current', [
170
- 'audit_params' => [
171
- 'user_login' => $user->user_login,
172
- 'session_id' => $current->token,
173
- ]
174
- ] );
 
 
 
175
  }
176
 
177
  unset( $this->currentWP );
71
  }
72
 
73
  if ( is_array( $parsed ) && !empty( $parsed[ 'token' ] ) ) {
 
74
 
75
+ $user = $WPUsers->getCurrentWpUser();
76
+ $userID = $user instanceof \WP_User ? $user->ID : $this->getCapturedUserID();
77
+
78
+ if ( !empty( $userID ) ) {
79
+ $manager = \WP_Session_Tokens::get_instance( $userID );
80
+ $session = $manager->get( $parsed[ 'token' ] );
81
+ if ( is_array( $session ) ) {
82
+
83
+ // Ensure the correct IP is stored
84
+ $srvIP = Services::IP();
85
+ $ip = $srvIP->getRequestIp();
86
+ if ( !empty( $ip ) && ( empty( $session[ 'ip' ] ) || !$srvIP->checkIp( $ip, $session[ 'ip' ] ) ) ) {
87
+ $session[ 'ip' ] = $ip;
88
+ }
89
+
90
+ $shieldSessionMeta = $session[ 'shield' ] ?? [];
91
+ $shieldSessionMeta[ 'user_id' ] = $userID;
92
+ $shieldSessionMeta[ 'last_activity_at' ] = Services::Request()->ts();
93
+ if ( empty( $shieldSessionMeta[ 'unique' ] ) ) {
94
+ $shieldSessionMeta[ 'unique' ] = uniqid();
95
+ }
96
+
97
+ $session[ 'shield' ] = $shieldSessionMeta;
98
+ $manager->update( $parsed[ 'token' ], $session );
99
+
100
+ // all that follows should not be stored
101
+ $session[ 'token' ] = $parsed[ 'token' ];
102
+ // This is a copy of \WP_Session_Tokens::hash_token(). They made it private, cuz that's helpful.
103
+ $session[ 'hashed_token' ] = function_exists( 'hash' ) ? hash( 'sha256', $parsed[ 'token' ] ) : sha1( $parsed[ 'token' ] );
104
+ $session[ 'valid' ] = true;
105
+ $this->currentWP->applyFromArray( $session );
106
+
107
+ // Update User Last Seen IP.
108
+ try {
109
+ $userMeta = $this->getCon()->getUserMeta( $WPUsers->getUserById( $userID ) );
110
+ if ( !empty( $userMeta ) ) {
111
+ $userMeta->record->ip_ref = ( new IPRecords() )
112
+ ->setMod( $this->getCon()->getModule_Data() )
113
+ ->loadIP( $session[ 'ip' ], true )
114
+ ->id;
115
+ }
116
+ }
117
+ catch ( \Exception $e ) {
118
+ }
119
  }
120
  }
121
  }
142
  }
143
 
144
  public function updateSessionParameter( string $key, $value ) {
 
145
  $current = $this->getCurrentWP();
146
  if ( $current->valid ) {
147
+
148
+ $user = Services::WpUsers()->getCurrentWpUser();
149
+ $userID = $user instanceof \WP_User ? $user->ID : ( $current->shield[ 'user_id' ] ?? 0 );
150
+ if ( !empty( $userID ) ) {
151
+ $shield = $current->shield;
152
+ $shield[ $key ] = $value;
153
+ $current->shield = $shield;
154
+ \WP_Session_Tokens::get_instance( $userID )
155
+ ->update(
156
+ $current->token,
157
+ array_diff_key( $current->getRawData(), array_flip( [
158
+ 'token',
159
+ 'hashed_token',
160
+ 'valid'
161
+ ] ) )
162
+ );
163
+ }
164
  }
165
  }
166
 
176
 
177
  if ( $current->valid ) {
178
  $user = Services::WpUsers()->getCurrentWpUser();
179
+ $userID = $user instanceof \WP_User ? $user->ID : ( $current->shield[ 'user_id' ] ?? 0 );
180
+ if ( !empty( $userID ) ) {
181
+ \WP_Session_Tokens::get_instance( $userID )->destroy( $current->token );
182
+ $this->getCon()->fireEvent( 'session_terminate_current', [
183
+ 'audit_params' => [
184
+ 'user_login' => $user->user_login,
185
+ 'session_id' => $current->token,
186
+ ]
187
+ ] );
188
+ }
189
  }
190
 
191
  unset( $this->currentWP );
src/lib/src/Utilities/Consumer/WpLoginCapture.php CHANGED
@@ -26,6 +26,11 @@ trait WpLoginCapture {
26
  */
27
  private $loggedInCookie = '';
28
 
 
 
 
 
 
29
  abstract protected function captureLogin( \WP_User $user );
30
 
31
  protected function getLoginPassword() :string {
@@ -46,6 +51,10 @@ trait WpLoginCapture {
46
  return is_string( $cookie ) ? $cookie : '';
47
  }
48
 
 
 
 
 
49
  protected function isCaptureApplicationLogin() :bool {
50
  return $this->isCaptureApplicationLogin;
51
  }
@@ -56,7 +65,6 @@ trait WpLoginCapture {
56
 
57
  /**
58
  * By default, will only capture logins if it's not an API request, or it's set to capture api requests also.
59
- * @return bool
60
  */
61
  protected function isLoginToBeCaptured() :bool {
62
  return !Services::WpGeneral()->isApplicationPasswordApiRequest() || $this->isCaptureApplicationLogin();
@@ -104,6 +112,7 @@ trait WpLoginCapture {
104
  && $this->isLoginToBeCaptured()
105
  && ( $this->allowMultipleCapture || !$this->isLoginCaptured() ) ) {
106
  $this->setLoginCaptured();
 
107
  $this->captureLogin( $user );
108
  }
109
  }
@@ -117,6 +126,7 @@ trait WpLoginCapture {
117
  && $this->isLoginToBeCaptured()
118
  && ( $this->allowMultipleCapture || !$this->isLoginCaptured() ) ) {
119
  $this->setLoginCaptured();
 
120
  $this->captureLogin( $user );
121
  }
122
  }
26
  */
27
  private $loggedInCookie = '';
28
 
29
+ /**
30
+ * @var int
31
+ */
32
+ private $capturedUserID = null;
33
+
34
  abstract protected function captureLogin( \WP_User $user );
35
 
36
  protected function getLoginPassword() :string {
51
  return is_string( $cookie ) ? $cookie : '';
52
  }
53
 
54
+ protected function getCapturedUserID() :int {
55
+ return is_int( $this->capturedUserID ) ? $this->capturedUserID : 0;
56
+ }
57
+
58
  protected function isCaptureApplicationLogin() :bool {
59
  return $this->isCaptureApplicationLogin;
60
  }
65
 
66
  /**
67
  * By default, will only capture logins if it's not an API request, or it's set to capture api requests also.
 
68
  */
69
  protected function isLoginToBeCaptured() :bool {
70
  return !Services::WpGeneral()->isApplicationPasswordApiRequest() || $this->isCaptureApplicationLogin();
112
  && $this->isLoginToBeCaptured()
113
  && ( $this->allowMultipleCapture || !$this->isLoginCaptured() ) ) {
114
  $this->setLoginCaptured();
115
+ $this->capturedUserID = $user->ID;
116
  $this->captureLogin( $user );
117
  }
118
  }
126
  && $this->isLoginToBeCaptured()
127
  && ( $this->allowMultipleCapture || !$this->isLoginCaptured() ) ) {
128
  $this->setLoginCaptured();
129
+ $this->capturedUserID = $user->ID;
130
  $this->captureLogin( $user );
131
  }
132
  }