Two-Factor - Version 0.5.2

Version Description

Download this release

Release Info

Developer kasparsd
Plugin Icon 128x128 Two-Factor
Version 0.5.2
Comparing to
See all releases

Code changes from version 0.7.0 to 0.5.2

class-two-factor-compat.php CHANGED
@@ -1,9 +1,4 @@
1
  <?php
2
- /**
3
- * A compatibility layer for some of the most popular plugins.
4
- *
5
- * @package Two_Factor
6
- */
7
 
8
  /**
9
  * A compatibility layer for some of the most popular plugins.
@@ -35,9 +30,7 @@ class Two_Factor_Compat {
35
  * @return boolean
36
  */
37
  public function jetpack_rememberme( $rememberme ) {
38
- $action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING );
39
-
40
- if ( 'jetpack-sso' === $action && $this->jetpack_is_sso_active() ) {
41
  return true;
42
  }
43
 
1
  <?php
 
 
 
 
 
2
 
3
  /**
4
  * A compatibility layer for some of the most popular plugins.
30
  * @return boolean
31
  */
32
  public function jetpack_rememberme( $rememberme ) {
33
+ if ( isset( $_GET['action'] ) && 'jetpack-sso' === $_GET['action'] && $this->jetpack_is_sso_active() ) {
 
 
34
  return true;
35
  }
36
 
class-two-factor-core.php CHANGED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Two Factore Core Class.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  /**
9
  * Class for creating two factor authorization.
10
  *
@@ -60,8 +54,6 @@ class Two_Factor_Core {
60
  /**
61
  * Set up filters and actions.
62
  *
63
- * @param object $compat A compaitbility later for plugins.
64
- *
65
  * @since 0.1-dev
66
  */
67
  public static function add_hooks( $compat ) {
@@ -114,11 +106,11 @@ class Two_Factor_Core {
114
  */
115
  public static function get_providers() {
116
  $providers = array(
117
- 'Two_Factor_Email' => TWO_FACTOR_DIR . 'providers/class-two-factor-email.php',
118
- 'Two_Factor_Totp' => TWO_FACTOR_DIR . 'providers/class-two-factor-totp.php',
119
- 'Two_Factor_FIDO_U2F' => TWO_FACTOR_DIR . 'providers/class-two-factor-fido-u2f.php',
120
- 'Two_Factor_Backup_Codes' => TWO_FACTOR_DIR . 'providers/class-two-factor-backup-codes.php',
121
- 'Two_Factor_Dummy' => TWO_FACTOR_DIR . 'providers/class-two-factor-dummy.php',
122
  );
123
 
124
  /**
@@ -135,20 +127,18 @@ class Two_Factor_Core {
135
  // FIDO U2F is PHP 5.3+ only.
136
  if ( isset( $providers['Two_Factor_FIDO_U2F'] ) && version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
137
  unset( $providers['Two_Factor_FIDO_U2F'] );
138
- trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
139
- sprintf(
140
  /* translators: %s: version number */
141
- __( 'FIDO U2F is not available because you are using PHP %s. (Requires 5.3 or greater)', 'two-factor' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
142
- PHP_VERSION
143
- )
144
- );
145
  }
146
 
147
  /**
148
  * For each filtered provider,
149
  */
150
  foreach ( $providers as $class => $path ) {
151
- include_once $path;
152
 
153
  /**
154
  * Confirm that it's been successfully included before instantiating.
@@ -277,7 +267,7 @@ class Two_Factor_Core {
277
  * @return void
278
  */
279
  public static function trigger_user_settings_action() {
280
- $action = filter_input( INPUT_GET, self::USER_SETTINGS_ACTION_QUERY_VAR, FILTER_SANITIZE_STRING );
281
  $user_id = self::current_user_being_edited();
282
 
283
  if ( ! empty( $action ) && self::is_valid_user_action( $user_id, $action ) ) {
@@ -351,7 +341,7 @@ class Two_Factor_Core {
351
  $configured_providers = array();
352
 
353
  foreach ( $providers as $classname => $provider ) {
354
- if ( in_array( $classname, $enabled_providers, true ) && $provider->is_available_for_user( $user ) ) {
355
  $configured_providers[ $classname ] = $provider;
356
  }
357
  }
@@ -536,33 +526,29 @@ class Two_Factor_Core {
536
  * @since 0.1-dev
537
  */
538
  public static function backup_2fa() {
539
- $wp_auth_id = filter_input( INPUT_GET, 'wp-auth-id', FILTER_SANITIZE_NUMBER_INT );
540
- $nonce = filter_input( INPUT_GET, 'wp-auth-nonce', FILTER_SANITIZE_STRING );
541
- $provider = filter_input( INPUT_GET, 'provider', FILTER_SANITIZE_STRING );
542
-
543
- if ( ! $wp_auth_id || ! $nonce || ! $provider ) {
544
  return;
545
  }
546
 
547
- $user = get_userdata( $wp_auth_id );
548
  if ( ! $user ) {
549
  return;
550
  }
551
 
 
552
  if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
553
  wp_safe_redirect( get_bloginfo( 'url' ) );
554
  exit;
555
  }
556
 
557
  $providers = self::get_available_providers_for_user( $user );
558
- if ( isset( $providers[ $provider ] ) ) {
559
- $provider = $providers[ $provider ];
560
  } else {
561
  wp_die( esc_html__( 'Cheatin&#8217; uh?', 'two-factor' ), 403 );
562
  }
563
 
564
- $redirect_to = filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_URL );
565
- self::login_html( $user, $nonce, $redirect_to, '', $provider );
566
 
567
  exit;
568
  }
@@ -588,14 +574,14 @@ class Two_Factor_Core {
588
  $provider_class = get_class( $provider );
589
 
590
  $available_providers = self::get_available_providers_for_user( $user );
591
- $backup_providers = array_diff_key( $available_providers, array( $provider_class => null ) );
592
- $interim_login = isset( $_REQUEST['interim-login'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
593
 
594
  $rememberme = intval( self::rememberme() );
595
 
596
  if ( ! function_exists( 'login_header' ) ) {
597
  // We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
598
- include_once TWO_FACTOR_DIR . 'includes/function.login-header.php';
599
  }
600
 
601
  login_header();
@@ -609,11 +595,11 @@ class Two_Factor_Core {
609
  <input type="hidden" name="provider" id="provider" value="<?php echo esc_attr( $provider_class ); ?>" />
610
  <input type="hidden" name="wp-auth-id" id="wp-auth-id" value="<?php echo esc_attr( $user->ID ); ?>" />
611
  <input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo esc_attr( $login_nonce ); ?>" />
612
- <?php if ( $interim_login ) { ?>
613
  <input type="hidden" name="interim-login" value="1" />
614
- <?php } else { ?>
615
  <input type="hidden" name="redirect_to" value="<?php echo esc_attr( $redirect_to ); ?>" />
616
- <?php } ?>
617
  <input type="hidden" name="rememberme" id="rememberme" value="<?php echo esc_attr( $rememberme ); ?>" />
618
 
619
  <?php $provider->authentication_page( $user ); ?>
@@ -622,8 +608,8 @@ class Two_Factor_Core {
622
  <?php
623
  if ( 1 === count( $backup_providers ) ) :
624
  $backup_classname = key( $backup_providers );
625
- $backup_provider = $backup_providers[ $backup_classname ];
626
- $login_url = self::login_url(
627
  array(
628
  'action' => 'backup_2fa',
629
  'provider' => $backup_classname,
@@ -717,8 +703,7 @@ class Two_Factor_Core {
717
 
718
  <?php
719
  /** This action is documented in wp-login.php */
720
- do_action( 'login_footer' );
721
- ?>
722
  <div class="clear"></div>
723
  </body>
724
  </html>
@@ -752,11 +737,11 @@ class Two_Factor_Core {
752
  * @return array
753
  */
754
  public static function create_login_nonce( $user_id ) {
755
- $login_nonce = array();
756
  try {
757
  $login_nonce['key'] = bin2hex( random_bytes( 32 ) );
758
- } catch ( Exception $ex ) {
759
- $login_nonce['key'] = wp_hash( $user_id . wp_rand() . microtime(), 'nonce' );
760
  }
761
  $login_nonce['expiration'] = time() + HOUR_IN_SECONDS;
762
 
@@ -808,28 +793,25 @@ class Two_Factor_Core {
808
  * @since 0.1-dev
809
  */
810
  public static function login_form_validate_2fa() {
811
- $wp_auth_id = filter_input( INPUT_POST, 'wp-auth-id', FILTER_SANITIZE_NUMBER_INT );
812
- $nonce = filter_input( INPUT_POST, 'wp-auth-nonce', FILTER_SANITIZE_STRING );
813
-
814
- if ( ! $wp_auth_id || ! $nonce ) {
815
  return;
816
  }
817
 
818
- $user = get_userdata( $wp_auth_id );
819
  if ( ! $user ) {
820
  return;
821
  }
822
 
 
823
  if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
824
  wp_safe_redirect( get_bloginfo( 'url' ) );
825
  exit;
826
  }
827
 
828
- $provider = filter_input( INPUT_POST, 'provider', FILTER_SANITIZE_STRING );
829
- if ( $provider ) {
830
  $providers = self::get_available_providers_for_user( $user );
831
- if ( isset( $providers[ $provider ] ) ) {
832
- $provider = $providers[ $provider ];
833
  } else {
834
  wp_die( esc_html__( 'Cheatin&#8217; uh?', 'two-factor' ), 403 );
835
  }
@@ -874,7 +856,7 @@ class Two_Factor_Core {
874
 
875
  // Must be global because that's how login_header() uses it.
876
  global $interim_login;
877
- $interim_login = isset( $_REQUEST['interim-login'] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited,WordPress.Security.NonceVerification.Recommended
878
 
879
  if ( $interim_login ) {
880
  $customize_login = isset( $_REQUEST['customize-login'] );
@@ -882,16 +864,14 @@ class Two_Factor_Core {
882
  wp_enqueue_script( 'customize-base' );
883
  }
884
  $message = '<p class="message">' . __( 'You have logged in successfully.', 'two-factor' ) . '</p>';
885
- $interim_login = 'success'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
886
- login_header( '', $message );
887
- ?>
888
  </div>
889
  <?php
890
  /** This action is documented in wp-login.php */
891
- do_action( 'login_footer' );
892
- ?>
893
  <?php if ( $customize_login ) : ?>
894
- <script type="text/javascript">setTimeout( function(){ new wp.customize.Messenger({ url: '<?php echo esc_url( wp_customize_url() ); ?>', channel: 'login' }).send('login') }, 1000 );</script>
895
  <?php endif; ?>
896
  </body></html>
897
  <?php
@@ -947,10 +927,10 @@ class Two_Factor_Core {
947
  * @param WP_User $user WP_User object of the logged-in user.
948
  */
949
  public static function user_two_factor_options( $user ) {
950
- wp_enqueue_style( 'user-edit-2fa', plugins_url( 'user-edit.css', __FILE__ ), array(), TWO_FACTOR_VERSION );
951
 
952
  $enabled_providers = array_keys( self::get_available_providers_for_user( $user ) );
953
- $primary_provider = self::get_primary_provider_for_user( $user->ID );
954
 
955
  if ( ! empty( $primary_provider ) && is_object( $primary_provider ) ) {
956
  $primary_provider_key = get_class( $primary_provider );
@@ -979,24 +959,11 @@ class Two_Factor_Core {
979
  <tbody>
980
  <?php foreach ( self::get_providers() as $class => $object ) : ?>
981
  <tr>
982
- <th scope="row"><input type="checkbox" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php echo esc_attr( $class ); ?>" <?php checked( in_array( $class, $enabled_providers, true ) ); ?> /></th>
983
  <th scope="row"><input type="radio" name="<?php echo esc_attr( self::PROVIDER_USER_META_KEY ); ?>" value="<?php echo esc_attr( $class ); ?>" <?php checked( $class, $primary_provider_key ); ?> /></th>
984
  <td>
985
- <?php
986
- $object->print_label();
987
-
988
- /**
989
- * Fires after user options are shown.
990
- *
991
- * Use the {@see 'two_factor_user_options_' . $class } hook instead.
992
- *
993
- * @deprecated 0.7.0
994
- *
995
- * @param WP_User $user The user.
996
- */
997
- do_action_deprecated( 'two-factor-user-options-' . $class, array( $user ), '0.7.0', 'two_factor_user_options_' . $class );
998
- do_action( 'two_factor_user_options_' . $class, $user );
999
- ?>
1000
  </td>
1001
  </tr>
1002
  <?php endforeach; ?>
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Class for creating two factor authorization.
4
  *
54
  /**
55
  * Set up filters and actions.
56
  *
 
 
57
  * @since 0.1-dev
58
  */
59
  public static function add_hooks( $compat ) {
106
  */
107
  public static function get_providers() {
108
  $providers = array(
109
+ 'Two_Factor_Email' => TWO_FACTOR_DIR . 'providers/class.two-factor-email.php',
110
+ 'Two_Factor_Totp' => TWO_FACTOR_DIR . 'providers/class.two-factor-totp.php',
111
+ 'Two_Factor_FIDO_U2F' => TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f.php',
112
+ 'Two_Factor_Backup_Codes' => TWO_FACTOR_DIR . 'providers/class.two-factor-backup-codes.php',
113
+ 'Two_Factor_Dummy' => TWO_FACTOR_DIR . 'providers/class.two-factor-dummy.php',
114
  );
115
 
116
  /**
127
  // FIDO U2F is PHP 5.3+ only.
128
  if ( isset( $providers['Two_Factor_FIDO_U2F'] ) && version_compare( PHP_VERSION, '5.3.0', '<' ) ) {
129
  unset( $providers['Two_Factor_FIDO_U2F'] );
130
+ trigger_error( sprintf( // WPCS: XSS OK.
 
131
  /* translators: %s: version number */
132
+ __( 'FIDO U2F is not available because you are using PHP %s. (Requires 5.3 or greater)', 'two-factor' ),
133
+ PHP_VERSION
134
+ ) );
 
135
  }
136
 
137
  /**
138
  * For each filtered provider,
139
  */
140
  foreach ( $providers as $class => $path ) {
141
+ include_once( $path );
142
 
143
  /**
144
  * Confirm that it's been successfully included before instantiating.
267
  * @return void
268
  */
269
  public static function trigger_user_settings_action() {
270
+ $action = filter_input( INPUT_GET, self::USER_SETTINGS_ACTION_QUERY_VAR, FILTER_SANITIZE_STRING );
271
  $user_id = self::current_user_being_edited();
272
 
273
  if ( ! empty( $action ) && self::is_valid_user_action( $user_id, $action ) ) {
341
  $configured_providers = array();
342
 
343
  foreach ( $providers as $classname => $provider ) {
344
+ if ( in_array( $classname, $enabled_providers ) && $provider->is_available_for_user( $user ) ) {
345
  $configured_providers[ $classname ] = $provider;
346
  }
347
  }
526
  * @since 0.1-dev
527
  */
528
  public static function backup_2fa() {
529
+ if ( ! isset( $_GET['wp-auth-id'], $_GET['wp-auth-nonce'], $_GET['provider'] ) ) {
 
 
 
 
530
  return;
531
  }
532
 
533
+ $user = get_userdata( $_GET['wp-auth-id'] );
534
  if ( ! $user ) {
535
  return;
536
  }
537
 
538
+ $nonce = $_GET['wp-auth-nonce'];
539
  if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
540
  wp_safe_redirect( get_bloginfo( 'url' ) );
541
  exit;
542
  }
543
 
544
  $providers = self::get_available_providers_for_user( $user );
545
+ if ( isset( $providers[ $_GET['provider'] ] ) ) {
546
+ $provider = $providers[ $_GET['provider'] ];
547
  } else {
548
  wp_die( esc_html__( 'Cheatin&#8217; uh?', 'two-factor' ), 403 );
549
  }
550
 
551
+ self::login_html( $user, $_GET['wp-auth-nonce'], $_GET['redirect_to'], '', $provider );
 
552
 
553
  exit;
554
  }
574
  $provider_class = get_class( $provider );
575
 
576
  $available_providers = self::get_available_providers_for_user( $user );
577
+ $backup_providers = array_diff_key( $available_providers, array( $provider_class => null ) );
578
+ $interim_login = isset( $_REQUEST['interim-login'] ); // WPCS: CSRF ok.
579
 
580
  $rememberme = intval( self::rememberme() );
581
 
582
  if ( ! function_exists( 'login_header' ) ) {
583
  // We really should migrate login_header() out of `wp-login.php` so it can be called from an includes file.
584
+ include_once( TWO_FACTOR_DIR . 'includes/function.login-header.php' );
585
  }
586
 
587
  login_header();
595
  <input type="hidden" name="provider" id="provider" value="<?php echo esc_attr( $provider_class ); ?>" />
596
  <input type="hidden" name="wp-auth-id" id="wp-auth-id" value="<?php echo esc_attr( $user->ID ); ?>" />
597
  <input type="hidden" name="wp-auth-nonce" id="wp-auth-nonce" value="<?php echo esc_attr( $login_nonce ); ?>" />
598
+ <?php if ( $interim_login ) { ?>
599
  <input type="hidden" name="interim-login" value="1" />
600
+ <?php } else { ?>
601
  <input type="hidden" name="redirect_to" value="<?php echo esc_attr( $redirect_to ); ?>" />
602
+ <?php } ?>
603
  <input type="hidden" name="rememberme" id="rememberme" value="<?php echo esc_attr( $rememberme ); ?>" />
604
 
605
  <?php $provider->authentication_page( $user ); ?>
608
  <?php
609
  if ( 1 === count( $backup_providers ) ) :
610
  $backup_classname = key( $backup_providers );
611
+ $backup_provider = $backup_providers[ $backup_classname ];
612
+ $login_url = self::login_url(
613
  array(
614
  'action' => 'backup_2fa',
615
  'provider' => $backup_classname,
703
 
704
  <?php
705
  /** This action is documented in wp-login.php */
706
+ do_action( 'login_footer' ); ?>
 
707
  <div class="clear"></div>
708
  </body>
709
  </html>
737
  * @return array
738
  */
739
  public static function create_login_nonce( $user_id ) {
740
+ $login_nonce = array();
741
  try {
742
  $login_nonce['key'] = bin2hex( random_bytes( 32 ) );
743
+ } catch (Exception $ex) {
744
+ $login_nonce['key'] = wp_hash( $user_id . mt_rand() . microtime(), 'nonce' );
745
  }
746
  $login_nonce['expiration'] = time() + HOUR_IN_SECONDS;
747
 
793
  * @since 0.1-dev
794
  */
795
  public static function login_form_validate_2fa() {
796
+ if ( ! isset( $_POST['wp-auth-id'], $_POST['wp-auth-nonce'] ) ) {
 
 
 
797
  return;
798
  }
799
 
800
+ $user = get_userdata( $_POST['wp-auth-id'] );
801
  if ( ! $user ) {
802
  return;
803
  }
804
 
805
+ $nonce = $_POST['wp-auth-nonce'];
806
  if ( true !== self::verify_login_nonce( $user->ID, $nonce ) ) {
807
  wp_safe_redirect( get_bloginfo( 'url' ) );
808
  exit;
809
  }
810
 
811
+ if ( isset( $_POST['provider'] ) ) {
 
812
  $providers = self::get_available_providers_for_user( $user );
813
+ if ( isset( $providers[ $_POST['provider'] ] ) ) {
814
+ $provider = $providers[ $_POST['provider'] ];
815
  } else {
816
  wp_die( esc_html__( 'Cheatin&#8217; uh?', 'two-factor' ), 403 );
817
  }
856
 
857
  // Must be global because that's how login_header() uses it.
858
  global $interim_login;
859
+ $interim_login = isset( $_REQUEST['interim-login'] ); // WPCS: override ok.
860
 
861
  if ( $interim_login ) {
862
  $customize_login = isset( $_REQUEST['customize-login'] );
864
  wp_enqueue_script( 'customize-base' );
865
  }
866
  $message = '<p class="message">' . __( 'You have logged in successfully.', 'two-factor' ) . '</p>';
867
+ $interim_login = 'success'; // WPCS: override ok.
868
+ login_header( '', $message ); ?>
 
869
  </div>
870
  <?php
871
  /** This action is documented in wp-login.php */
872
+ do_action( 'login_footer' ); ?>
 
873
  <?php if ( $customize_login ) : ?>
874
+ <script type="text/javascript">setTimeout( function(){ new wp.customize.Messenger({ url: '<?php echo wp_customize_url(); /* WPCS: XSS OK. */ ?>', channel: 'login' }).send('login') }, 1000 );</script>
875
  <?php endif; ?>
876
  </body></html>
877
  <?php
927
  * @param WP_User $user WP_User object of the logged-in user.
928
  */
929
  public static function user_two_factor_options( $user ) {
930
+ wp_enqueue_style( 'user-edit-2fa', plugins_url( 'user-edit.css', __FILE__ ) );
931
 
932
  $enabled_providers = array_keys( self::get_available_providers_for_user( $user ) );
933
+ $primary_provider = self::get_primary_provider_for_user( $user->ID );
934
 
935
  if ( ! empty( $primary_provider ) && is_object( $primary_provider ) ) {
936
  $primary_provider_key = get_class( $primary_provider );
959
  <tbody>
960
  <?php foreach ( self::get_providers() as $class => $object ) : ?>
961
  <tr>
962
+ <th scope="row"><input type="checkbox" name="<?php echo esc_attr( self::ENABLED_PROVIDERS_USER_META_KEY ); ?>[]" value="<?php echo esc_attr( $class ); ?>" <?php checked( in_array( $class, $enabled_providers ) ); ?> /></th>
963
  <th scope="row"><input type="radio" name="<?php echo esc_attr( self::PROVIDER_USER_META_KEY ); ?>" value="<?php echo esc_attr( $class ); ?>" <?php checked( $class, $primary_provider_key ); ?> /></th>
964
  <td>
965
+ <?php $object->print_label(); ?>
966
+ <?php do_action( 'two-factor-user-options-' . $class, $user ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
967
  </td>
968
  </tr>
969
  <?php endforeach; ?>
docker-compose.yml DELETED
@@ -1,11 +0,0 @@
1
- version: '3.6'
2
-
3
- services:
4
-
5
- wpdevlib:
6
- build: ./tests/docker/wp-dev-lib
7
- working_dir: /var/www/html
8
- volumes:
9
- - .:/var/www/html
10
- environment:
11
- CHECK_SCOPE: all
 
 
 
 
 
 
 
 
 
 
 
providers/{class-two-factor-backup-codes.php → class.two-factor-backup-codes.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Class for creating a backup codes provider.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  /**
9
  * Class for creating a backup codes provider.
10
  *
@@ -33,11 +27,11 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
33
  *
34
  * @since 0.1-dev
35
  */
36
- public static function get_instance() {
37
  static $instance;
38
  $class = __CLASS__;
39
  if ( ! is_a( $instance, $class ) ) {
40
- $instance = new $class();
41
  }
42
  return $instance;
43
  }
@@ -48,7 +42,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
48
  * @since 0.1-dev
49
  */
50
  protected function __construct() {
51
- add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) );
52
  add_action( 'admin_notices', array( $this, 'admin_notices' ) );
53
  add_action( 'wp_ajax_two_factor_backup_codes_generate', array( $this, 'ajax_generate_json' ) );
54
 
@@ -64,7 +58,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
64
  $user = wp_get_current_user();
65
 
66
  // Return if the provider is not enabled.
67
- if ( ! in_array( __CLASS__, Two_Factor_Core::get_enabled_providers_for_user( $user->ID ), true ) ) {
68
  return;
69
  }
70
 
@@ -77,13 +71,9 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
77
  <p>
78
  <span>
79
  <?php
80
- wp_kses(
81
- sprintf(
82
- /* translators: %s: URL for code regeneration */
83
- __( 'Two-Factor: You are out of backup codes and need to <a href="%s">regenerate!</a>', 'two-factor' ),
84
- esc_url( get_edit_user_link( $user->ID ) . '#two-factor-backup-codes' )
85
- ),
86
- array( 'a' => array( 'href' => true ) )
87
  );
88
  ?>
89
  <span>
@@ -126,23 +116,19 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
126
  */
127
  public function user_options( $user ) {
128
  $ajax_nonce = wp_create_nonce( 'two-factor-backup-codes-generate-json-' . $user->ID );
129
- $count = self::codes_remaining_for_user( $user );
130
  ?>
131
  <p id="two-factor-backup-codes">
132
  <button type="button" class="button button-two-factor-backup-codes-generate button-secondary hide-if-no-js">
133
  <?php esc_html_e( 'Generate Verification Codes', 'two-factor' ); ?>
134
  </button>
135
- <span class="two-factor-backup-codes-count">
136
- <?php
137
- echo esc_html(
138
- sprintf(
139
  /* translators: %s: count */
140
- _n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'two-factor' ),
141
- $count
142
- )
143
- );
144
- ?>
145
- </span>
146
  </p>
147
  <div class="two-factor-backup-codes-wrapper" style="display:none;">
148
  <ol class="two-factor-backup-codes-unused-codes"></ol>
@@ -204,7 +190,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
204
  * @return array
205
  */
206
  public function generate_codes( $user, $args = '' ) {
207
- $codes = array();
208
  $codes_hashed = array();
209
 
210
  // Check for arguments.
@@ -220,9 +206,9 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
220
  }
221
 
222
  for ( $i = 0; $i < $num_codes; $i++ ) {
223
- $code = $this->get_code();
224
  $codes_hashed[] = wp_hash_password( $code );
225
- $codes[] = $code;
226
  unset( $code );
227
  }
228
 
@@ -238,13 +224,13 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
238
  * @since 0.1-dev
239
  */
240
  public function ajax_generate_json() {
241
- $user = get_user_by( 'id', filter_input( INPUT_POST, 'user_id', FILTER_SANITIZE_NUMBER_INT ) );
242
  check_ajax_referer( 'two-factor-backup-codes-generate-json-' . $user->ID, 'nonce' );
243
 
244
  // Setup the return data.
245
  $codes = $this->generate_codes( $user );
246
  $count = self::codes_remaining_for_user( $user );
247
- $i18n = array(
248
  /* translators: %s: count */
249
  'count' => esc_html( sprintf( _n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'two-factor' ), $count ) ),
250
  /* translators: %s: the site's domain */
@@ -252,12 +238,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
252
  );
253
 
254
  // Send the response.
255
- wp_send_json_success(
256
- array(
257
- 'codes' => $codes,
258
- 'i18n' => $i18n,
259
- )
260
- );
261
  }
262
 
263
  /**
@@ -282,7 +263,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
282
  * @param WP_User $user WP_User object of the logged-in user.
283
  */
284
  public function authentication_page( $user ) {
285
- require_once ABSPATH . '/wp-admin/includes/template.php';
286
  ?>
287
  <p><?php esc_html_e( 'Enter a backup verification code.', 'two-factor' ); ?></p><br/>
288
  <p>
@@ -304,8 +285,7 @@ class Two_Factor_Backup_Codes extends Two_Factor_Provider {
304
  * @return boolean
305
  */
306
  public function validate_authentication( $user ) {
307
- $backup_code = isset( $_POST['two-factor-backup-code'] ) ? sanitize_text_field( wp_unslash( $_POST['two-factor-backup-code'] ) ) : false;
308
- return $this->validate_code( $user, filter_var( $backup_code, FILTER_SANITIZE_STRING ) );
309
  }
310
 
311
  /**
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Class for creating a backup codes provider.
4
  *
27
  *
28
  * @since 0.1-dev
29
  */
30
+ static function get_instance() {
31
  static $instance;
32
  $class = __CLASS__;
33
  if ( ! is_a( $instance, $class ) ) {
34
+ $instance = new $class;
35
  }
36
  return $instance;
37
  }
42
  * @since 0.1-dev
43
  */
44
  protected function __construct() {
45
+ add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
46
  add_action( 'admin_notices', array( $this, 'admin_notices' ) );
47
  add_action( 'wp_ajax_two_factor_backup_codes_generate', array( $this, 'ajax_generate_json' ) );
48
 
58
  $user = wp_get_current_user();
59
 
60
  // Return if the provider is not enabled.
61
+ if ( ! in_array( __CLASS__, Two_Factor_Core::get_enabled_providers_for_user( $user->ID ) ) ) {
62
  return;
63
  }
64
 
71
  <p>
72
  <span>
73
  <?php
74
+ printf( // WPCS: XSS OK.
75
+ __( 'Two-Factor: You are out of backup codes and need to <a href="%s">regenerate!</a>', 'two-factor' ),
76
+ esc_url( get_edit_user_link( $user->ID ) . '#two-factor-backup-codes' )
 
 
 
 
77
  );
78
  ?>
79
  <span>
116
  */
117
  public function user_options( $user ) {
118
  $ajax_nonce = wp_create_nonce( 'two-factor-backup-codes-generate-json-' . $user->ID );
119
+ $count = self::codes_remaining_for_user( $user );
120
  ?>
121
  <p id="two-factor-backup-codes">
122
  <button type="button" class="button button-two-factor-backup-codes-generate button-secondary hide-if-no-js">
123
  <?php esc_html_e( 'Generate Verification Codes', 'two-factor' ); ?>
124
  </button>
125
+ <span class="two-factor-backup-codes-count"><?php
126
+ echo esc_html( sprintf(
 
 
127
  /* translators: %s: count */
128
+ _n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'two-factor' ),
129
+ $count
130
+ ) );
131
+ ?></span>
 
 
132
  </p>
133
  <div class="two-factor-backup-codes-wrapper" style="display:none;">
134
  <ol class="two-factor-backup-codes-unused-codes"></ol>
190
  * @return array
191
  */
192
  public function generate_codes( $user, $args = '' ) {
193
+ $codes = array();
194
  $codes_hashed = array();
195
 
196
  // Check for arguments.
206
  }
207
 
208
  for ( $i = 0; $i < $num_codes; $i++ ) {
209
+ $code = $this->get_code();
210
  $codes_hashed[] = wp_hash_password( $code );
211
+ $codes[] = $code;
212
  unset( $code );
213
  }
214
 
224
  * @since 0.1-dev
225
  */
226
  public function ajax_generate_json() {
227
+ $user = get_user_by( 'id', sanitize_text_field( $_POST['user_id'] ) );
228
  check_ajax_referer( 'two-factor-backup-codes-generate-json-' . $user->ID, 'nonce' );
229
 
230
  // Setup the return data.
231
  $codes = $this->generate_codes( $user );
232
  $count = self::codes_remaining_for_user( $user );
233
+ $i18n = array(
234
  /* translators: %s: count */
235
  'count' => esc_html( sprintf( _n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'two-factor' ), $count ) ),
236
  /* translators: %s: the site's domain */
238
  );
239
 
240
  // Send the response.
241
+ wp_send_json_success( array( 'codes' => $codes, 'i18n' => $i18n ) );
 
 
 
 
 
242
  }
243
 
244
  /**
263
  * @param WP_User $user WP_User object of the logged-in user.
264
  */
265
  public function authentication_page( $user ) {
266
+ require_once( ABSPATH . '/wp-admin/includes/template.php' );
267
  ?>
268
  <p><?php esc_html_e( 'Enter a backup verification code.', 'two-factor' ); ?></p><br/>
269
  <p>
285
  * @return boolean
286
  */
287
  public function validate_authentication( $user ) {
288
+ return $this->validate_code( $user, $_POST['two-factor-backup-code'] );
 
289
  }
290
 
291
  /**
providers/{class-two-factor-dummy.php → class.two-factor-dummy.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Class for creating a dummy provider.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  /**
9
  * Class for creating a dummy provider.
10
  *
@@ -19,11 +13,11 @@ class Two_Factor_Dummy extends Two_Factor_Provider {
19
  *
20
  * @since 0.1-dev
21
  */
22
- public static function get_instance() {
23
  static $instance;
24
  $class = __CLASS__;
25
  if ( ! is_a( $instance, $class ) ) {
26
- $instance = new $class();
27
  }
28
  return $instance;
29
  }
@@ -34,7 +28,7 @@ class Two_Factor_Dummy extends Two_Factor_Provider {
34
  * @since 0.1-dev
35
  */
36
  protected function __construct() {
37
- add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) );
38
  return parent::__construct();
39
  }
40
 
@@ -55,7 +49,7 @@ class Two_Factor_Dummy extends Two_Factor_Provider {
55
  * @param WP_User $user WP_User object of the logged-in user.
56
  */
57
  public function authentication_page( $user ) {
58
- require_once ABSPATH . '/wp-admin/includes/template.php';
59
  ?>
60
  <p><?php esc_html_e( 'Are you really you?', 'two-factor' ); ?></p>
61
  <?php
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Class for creating a dummy provider.
4
  *
13
  *
14
  * @since 0.1-dev
15
  */
16
+ static function get_instance() {
17
  static $instance;
18
  $class = __CLASS__;
19
  if ( ! is_a( $instance, $class ) ) {
20
+ $instance = new $class;
21
  }
22
  return $instance;
23
  }
28
  * @since 0.1-dev
29
  */
30
  protected function __construct() {
31
+ add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
32
  return parent::__construct();
33
  }
34
 
49
  * @param WP_User $user WP_User object of the logged-in user.
50
  */
51
  public function authentication_page( $user ) {
52
+ require_once( ABSPATH . '/wp-admin/includes/template.php' );
53
  ?>
54
  <p><?php esc_html_e( 'Are you really you?', 'two-factor' ); ?></p>
55
  <?php
providers/{class-two-factor-email.php → class.two-factor-email.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Class for creating an email provider.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  /**
9
  * Class for creating an email provider.
10
  *
@@ -17,17 +11,10 @@ class Two_Factor_Email extends Two_Factor_Provider {
17
  /**
18
  * The user meta token key.
19
  *
20
- * @var string
21
  */
22
  const TOKEN_META_KEY = '_two_factor_email_token';
23
 
24
- /**
25
- * Store the timestamp when the token was generated.
26
- *
27
- * @var string
28
- */
29
- const TOKEN_META_KEY_TIMESTAMP = '_two_factor_email_token_timestamp';
30
-
31
  /**
32
  * Name of the input field used for code resend.
33
  *
@@ -40,11 +27,11 @@ class Two_Factor_Email extends Two_Factor_Provider {
40
  *
41
  * @since 0.1-dev
42
  */
43
- public static function get_instance() {
44
  static $instance;
45
  $class = __CLASS__;
46
  if ( ! is_a( $instance, $class ) ) {
47
- $instance = new $class();
48
  }
49
  return $instance;
50
  }
@@ -55,7 +42,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
55
  * @since 0.1-dev
56
  */
57
  protected function __construct() {
58
- add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) );
59
  return parent::__construct();
60
  }
61
 
@@ -78,10 +65,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
78
  */
79
  public function generate_token( $user_id ) {
80
  $token = $this->get_code();
81
-
82
- update_user_meta( $user_id, self::TOKEN_META_KEY_TIMESTAMP, time() );
83
  update_user_meta( $user_id, self::TOKEN_META_KEY, wp_hash( $token ) );
84
-
85
  return $token;
86
  }
87
 
@@ -96,65 +80,9 @@ class Two_Factor_Email extends Two_Factor_Provider {
96
 
97
  if ( ! empty( $hashed_token ) ) {
98
  return true;
99
- }
100
-
101
- return false;
102
- }
103
-
104
- /**
105
- * Has the user token validity timestamp expired.
106
- *
107
- * @param integer $user_id User ID.
108
- *
109
- * @return boolean
110
- */
111
- public function user_token_has_expired( $user_id ) {
112
- $token_lifetime = $this->user_token_lifetime( $user_id );
113
- $token_ttl = $this->user_token_ttl( $user_id );
114
-
115
- // Invalid token lifetime is considered an expired token.
116
- if ( is_int( $token_lifetime ) && $token_lifetime <= $token_ttl ) {
117
  return false;
118
  }
119
-
120
- return true;
121
- }
122
-
123
- /**
124
- * Get the lifetime of a user token in seconds.
125
- *
126
- * @param integer $user_id User ID.
127
- *
128
- * @return integer|null Return `null` if the lifetime can't be measured.
129
- */
130
- public function user_token_lifetime( $user_id ) {
131
- $timestamp = intval( get_user_meta( $user_id, self::TOKEN_META_KEY_TIMESTAMP, true ) );
132
-
133
- if ( ! empty( $timestamp ) ) {
134
- return time() - $timestamp;
135
- }
136
-
137
- return null;
138
- }
139
-
140
- /**
141
- * Return the token time-to-live for a user.
142
- *
143
- * @param integer $user_id User ID.
144
- *
145
- * @return integer
146
- */
147
- public function user_token_ttl( $user_id ) {
148
- $token_ttl = 15 * MINUTE_IN_SECONDS;
149
-
150
- /**
151
- * Number of seconds the token is considered valid
152
- * after the generation.
153
- *
154
- * @param integer $token_ttl Token time-to-live in seconds.
155
- * @param integer $user_id User ID.
156
- */
157
- return (int) apply_filters( 'two_factor_token_ttl', $token_ttl, $user_id );
158
  }
159
 
160
  /**
@@ -191,11 +119,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
191
  return false;
192
  }
193
 
194
- if ( $this->user_token_has_expired( $user_id ) ) {
195
- return false;
196
- }
197
-
198
- // Ensure the token can be used only once.
199
  $this->delete_token( $user_id );
200
 
201
  return true;
@@ -260,16 +184,16 @@ class Two_Factor_Email extends Two_Factor_Provider {
260
  return;
261
  }
262
 
263
- if ( ! $this->user_has_token( $user->ID ) || $this->user_token_has_expired( $user->ID ) ) {
264
  $this->generate_and_email_token( $user );
265
  }
266
 
267
- require_once ABSPATH . '/wp-admin/includes/template.php';
268
  ?>
269
  <p><?php esc_html_e( 'A verification code has been sent to the email address associated with your account.', 'two-factor' ); ?></p>
270
  <p>
271
  <label for="authcode"><?php esc_html_e( 'Verification Code:', 'two-factor' ); ?></label>
272
- <input type="tel" name="two-factor-email-code" id="authcode" class="input" value="" size="20" />
273
  <?php submit_button( __( 'Log In', 'two-factor' ) ); ?>
274
  </p>
275
  <p class="two-factor-email-resend">
@@ -317,10 +241,7 @@ class Two_Factor_Email extends Two_Factor_Provider {
317
  return false;
318
  }
319
 
320
- // Ensure there are no spaces or line breaks around the code.
321
- $code = trim( sanitize_text_field( $_REQUEST['two-factor-email-code'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, handled by the core method already.
322
-
323
- return $this->validate_token( $user->ID, $code );
324
  }
325
 
326
  /**
@@ -347,13 +268,11 @@ class Two_Factor_Email extends Two_Factor_Provider {
347
  ?>
348
  <div>
349
  <?php
350
- echo esc_html(
351
- sprintf(
352
  /* translators: %s: email address */
353
- __( 'Authentication codes will be sent to %s.', 'two-factor' ),
354
- $email
355
- )
356
- );
357
  ?>
358
  </div>
359
  <?php
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Class for creating an email provider.
4
  *
11
  /**
12
  * The user meta token key.
13
  *
14
+ * @type string
15
  */
16
  const TOKEN_META_KEY = '_two_factor_email_token';
17
 
 
 
 
 
 
 
 
18
  /**
19
  * Name of the input field used for code resend.
20
  *
27
  *
28
  * @since 0.1-dev
29
  */
30
+ static function get_instance() {
31
  static $instance;
32
  $class = __CLASS__;
33
  if ( ! is_a( $instance, $class ) ) {
34
+ $instance = new $class;
35
  }
36
  return $instance;
37
  }
42
  * @since 0.1-dev
43
  */
44
  protected function __construct() {
45
+ add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
46
  return parent::__construct();
47
  }
48
 
65
  */
66
  public function generate_token( $user_id ) {
67
  $token = $this->get_code();
 
 
68
  update_user_meta( $user_id, self::TOKEN_META_KEY, wp_hash( $token ) );
 
69
  return $token;
70
  }
71
 
80
 
81
  if ( ! empty( $hashed_token ) ) {
82
  return true;
83
+ } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  return false;
85
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
87
 
88
  /**
119
  return false;
120
  }
121
 
122
+ // Ensure that the token can't be re-used.
 
 
 
 
123
  $this->delete_token( $user_id );
124
 
125
  return true;
184
  return;
185
  }
186
 
187
+ if ( ! $this->user_has_token( $user->ID ) ) {
188
  $this->generate_and_email_token( $user );
189
  }
190
 
191
+ require_once( ABSPATH . '/wp-admin/includes/template.php' );
192
  ?>
193
  <p><?php esc_html_e( 'A verification code has been sent to the email address associated with your account.', 'two-factor' ); ?></p>
194
  <p>
195
  <label for="authcode"><?php esc_html_e( 'Verification Code:', 'two-factor' ); ?></label>
196
+ <input type="tel" name="two-factor-email-code" id="authcode" class="input" value="" size="20" pattern="[0-9]*" />
197
  <?php submit_button( __( 'Log In', 'two-factor' ) ); ?>
198
  </p>
199
  <p class="two-factor-email-resend">
241
  return false;
242
  }
243
 
244
+ return $this->validate_token( $user->ID, $_REQUEST['two-factor-email-code'] );
 
 
 
245
  }
246
 
247
  /**
268
  ?>
269
  <div>
270
  <?php
271
+ echo esc_html( sprintf(
 
272
  /* translators: %s: email address */
273
+ __( 'Authentication codes will be sent to %s.', 'two-factor' ),
274
+ $email
275
+ ) );
 
276
  ?>
277
  </div>
278
  <?php
providers/{class-two-factor-fido-u2f-admin-list-table.php → class.two-factor-fido-u2f-admin-list-table.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Class for displaying the list of security key items.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  // Load the parent class if it doesn't exist.
9
  if ( ! class_exists( 'WP_List_Table' ) ) {
10
  require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
@@ -41,10 +35,10 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
41
  * @since 0.1-dev
42
  */
43
  public function prepare_items() {
44
- $columns = $this->get_columns();
45
- $hidden = array();
46
- $sortable = array();
47
- $primary = 'name';
48
  $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
49
  }
50
 
@@ -61,20 +55,20 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
61
  protected function column_default( $item, $column_name ) {
62
  switch ( $column_name ) {
63
  case 'name':
64
- $out = '<div class="hidden" id="inline_' . esc_attr( $item->keyHandle ) . '">'; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
65
  $out .= '<div class="name">' . esc_html( $item->name ) . '</div>';
66
  $out .= '</div>';
67
 
68
  $actions = array(
69
  'rename hide-if-no-js' => Two_Factor_FIDO_U2F_Admin::rename_link( $item ),
70
- 'delete' => Two_Factor_FIDO_U2F_Admin::delete_link( $item ),
71
  );
72
 
73
  return esc_html( $item->name ) . $out . self::row_actions( $actions );
74
  case 'added':
75
- return gmdate( get_option( 'date_format', 'r' ), $item->added );
76
  case 'last_used':
77
- return gmdate( get_option( 'date_format', 'r' ), $item->last_used );
78
  default:
79
  return 'WTF^^?';
80
  }
@@ -102,7 +96,7 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
102
  */
103
  public function single_row( $item ) {
104
  ?>
105
- <tr id="key-<?php echo esc_attr( $item->keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase ?>">
106
  <?php $this->single_row_columns( $item ); ?>
107
  </tr>
108
  <?php
@@ -128,11 +122,7 @@ class Two_Factor_FIDO_U2F_Admin_List_Table extends WP_List_Table {
128
  </div>
129
  </fieldset>
130
  <?php
131
- $core_columns = array(
132
- 'name' => true,
133
- 'added' => true,
134
- 'last_used' => true,
135
- );
136
  list( $columns ) = $this->get_column_info();
137
  foreach ( $columns as $column_name => $column_display_name ) {
138
  if ( isset( $core_columns[ $column_name ] ) ) {
1
  <?php
 
 
 
 
 
 
2
  // Load the parent class if it doesn't exist.
3
  if ( ! class_exists( 'WP_List_Table' ) ) {
4
  require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
35
  * @since 0.1-dev
36
  */
37
  public function prepare_items() {
38
+ $columns = $this->get_columns();
39
+ $hidden = array();
40
+ $sortable = array();
41
+ $primary = 'name';
42
  $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
43
  }
44
 
55
  protected function column_default( $item, $column_name ) {
56
  switch ( $column_name ) {
57
  case 'name':
58
+ $out = '<div class="hidden" id="inline_' . esc_attr( $item->keyHandle ) . '">';
59
  $out .= '<div class="name">' . esc_html( $item->name ) . '</div>';
60
  $out .= '</div>';
61
 
62
  $actions = array(
63
  'rename hide-if-no-js' => Two_Factor_FIDO_U2F_Admin::rename_link( $item ),
64
+ 'delete' => Two_Factor_FIDO_U2F_Admin::delete_link( $item ),
65
  );
66
 
67
  return esc_html( $item->name ) . $out . self::row_actions( $actions );
68
  case 'added':
69
+ return date( get_option( 'date_format', 'r' ), $item->added );
70
  case 'last_used':
71
+ return date( get_option( 'date_format', 'r' ), $item->last_used );
72
  default:
73
  return 'WTF^^?';
74
  }
96
  */
97
  public function single_row( $item ) {
98
  ?>
99
+ <tr id="key-<?php echo esc_attr( $item->keyHandle ); ?>">
100
  <?php $this->single_row_columns( $item ); ?>
101
  </tr>
102
  <?php
122
  </div>
123
  </fieldset>
124
  <?php
125
+ $core_columns = array( 'name' => true, 'added' => true, 'last_used' => true );
 
 
 
 
126
  list( $columns ) = $this->get_column_info();
127
  foreach ( $columns as $column_name => $column_display_name ) {
128
  if ( isset( $core_columns[ $column_name ] ) ) {
providers/{class-two-factor-fido-u2f-admin.php → class.two-factor-fido-u2f-admin.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Class for registering & modifying FIDO U2F security keys.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  /**
9
  * Class for registering & modifying FIDO U2F security keys.
10
  *
@@ -30,13 +24,13 @@ class Two_Factor_FIDO_U2F_Admin {
30
  * @static
31
  */
32
  public static function add_hooks() {
33
- add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
34
  add_action( 'show_user_security_settings', array( __CLASS__, 'show_user_profile' ) );
35
- add_action( 'personal_options_update', array( __CLASS__, 'catch_submission' ), 0 );
36
- add_action( 'edit_user_profile_update', array( __CLASS__, 'catch_submission' ), 0 );
37
- add_action( 'load-profile.php', array( __CLASS__, 'catch_delete_security_key' ) );
38
- add_action( 'load-user-edit.php', array( __CLASS__, 'catch_delete_security_key' ) );
39
- add_action( 'wp_ajax_inline-save-key', array( __CLASS__, 'wp_ajax_inline_save' ) );
40
  }
41
 
42
  /**
@@ -50,7 +44,7 @@ class Two_Factor_FIDO_U2F_Admin {
50
  * @param string $hook Current page.
51
  */
52
  public static function enqueue_assets( $hook ) {
53
- if ( ! in_array( $hook, array( 'user-edit.php', 'profile.php' ), true ) ) {
54
  return;
55
  }
56
 
@@ -63,7 +57,7 @@ class Two_Factor_FIDO_U2F_Admin {
63
 
64
  // @todo Ensure that scripts don't fail because of missing u2fL10n.
65
  try {
66
- $data = Two_Factor_FIDO_U2F::$u2f->getRegisterData( $security_keys );
67
  list( $req,$sigs ) = $data;
68
 
69
  update_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, $req );
@@ -94,12 +88,12 @@ class Two_Factor_FIDO_U2F_Admin {
94
  'user_id' => $user_id,
95
  'register' => array(
96
  'request' => $req,
97
- 'sigs' => $sigs,
98
  ),
99
- 'text' => array(
100
- 'insert' => esc_html__( 'Now insert (and tap) your Security Key.', 'two-factor' ),
101
- 'error' => esc_html__( 'U2F request failed.', 'two-factor' ),
102
- 'error_codes' => array(
103
  // Map u2f.ErrorCodes to error messages.
104
  0 => esc_html__( 'Request OK.', 'two-factor' ),
105
  1 => esc_html__( 'Other U2F error.', 'two-factor' ),
@@ -208,8 +202,8 @@ class Two_Factor_FIDO_U2F_Admin {
208
  <p><a href="https://support.google.com/accounts/answer/6103523"><?php esc_html_e( 'You can find FIDO U2F Security Key devices for sale from here.', 'two-factor' ); ?></a></p>
209
 
210
  <?php
211
- require TWO_FACTOR_DIR . 'providers/class-two-factor-fido-u2f-admin-list-table.php';
212
- $u2f_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table();
213
  $u2f_list_table->items = $security_keys;
214
  $u2f_list_table->prepare_items();
215
  $u2f_list_table->display();
@@ -238,7 +232,7 @@ class Two_Factor_FIDO_U2F_Admin {
238
 
239
  try {
240
  $response = json_decode( stripslashes( $_POST['u2f_response'] ) );
241
- $reg = Two_Factor_FIDO_U2F::$u2f->doRegister( get_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, true ), $response );
242
  $reg->new = true;
243
 
244
  Two_Factor_FIDO_U2F::add_security_key( $user_id, $reg );
@@ -248,14 +242,9 @@ class Two_Factor_FIDO_U2F_Admin {
248
 
249
  delete_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY );
250
 
251
- wp_safe_redirect(
252
- add_query_arg(
253
- array(
254
- 'new_app_pass' => 1,
255
- ),
256
- wp_get_referer()
257
- ) . '#security-keys-section'
258
- );
259
  exit;
260
  }
261
  }
@@ -311,7 +300,7 @@ class Two_Factor_FIDO_U2F_Admin {
311
  * @return string
312
  */
313
  public static function delete_link( $item ) {
314
- $delete_link = add_query_arg( 'delete_security_key', $item->keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
315
  $delete_link = wp_nonce_url( $delete_link, "delete_security_key-{$item->keyHandle}", '_nonce_delete_security_key' );
316
  return sprintf( '<a href="%1$s">%2$s</a>', esc_url( $delete_link ), esc_html__( 'Delete', 'two-factor' ) );
317
  }
@@ -327,21 +316,21 @@ class Two_Factor_FIDO_U2F_Admin {
327
  public static function wp_ajax_inline_save() {
328
  check_ajax_referer( 'keyinlineeditnonce', '_inline_edit' );
329
 
330
- require TWO_FACTOR_DIR . 'providers/class-two-factor-fido-u2f-admin-list-table.php';
331
  $wp_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table();
332
 
333
  if ( ! isset( $_POST['keyHandle'] ) ) {
334
  wp_die();
335
  }
336
 
337
- $user_id = Two_Factor_Core::current_user_being_edited();
338
  $security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user_id );
339
  if ( ! $security_keys ) {
340
  wp_die();
341
  }
342
 
343
  foreach ( $security_keys as &$key ) {
344
- if ( $key->keyHandle === $_POST['keyHandle'] ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
345
  break;
346
  }
347
  }
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Class for registering & modifying FIDO U2F security keys.
4
  *
24
  * @static
25
  */
26
  public static function add_hooks() {
27
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
28
  add_action( 'show_user_security_settings', array( __CLASS__, 'show_user_profile' ) );
29
+ add_action( 'personal_options_update', array( __CLASS__, 'catch_submission' ), 0 );
30
+ add_action( 'edit_user_profile_update', array( __CLASS__, 'catch_submission' ), 0 );
31
+ add_action( 'load-profile.php', array( __CLASS__, 'catch_delete_security_key' ) );
32
+ add_action( 'load-user-edit.php', array( __CLASS__, 'catch_delete_security_key' ) );
33
+ add_action( 'wp_ajax_inline-save-key', array( __CLASS__, 'wp_ajax_inline_save' ) );
34
  }
35
 
36
  /**
44
  * @param string $hook Current page.
45
  */
46
  public static function enqueue_assets( $hook ) {
47
+ if ( ! in_array( $hook, array( 'user-edit.php', 'profile.php' ) ) ) {
48
  return;
49
  }
50
 
57
 
58
  // @todo Ensure that scripts don't fail because of missing u2fL10n.
59
  try {
60
+ $data = Two_Factor_FIDO_U2F::$u2f->getRegisterData( $security_keys );
61
  list( $req,$sigs ) = $data;
62
 
63
  update_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, $req );
88
  'user_id' => $user_id,
89
  'register' => array(
90
  'request' => $req,
91
+ 'sigs' => $sigs,
92
  ),
93
+ 'text' => array(
94
+ 'insert' => esc_html__( 'Now insert (and tap) your Security Key.', 'two-factor' ),
95
+ 'error' => esc_html__( 'U2F request failed.', 'two-factor' ),
96
+ 'error_codes' => array(
97
  // Map u2f.ErrorCodes to error messages.
98
  0 => esc_html__( 'Request OK.', 'two-factor' ),
99
  1 => esc_html__( 'Other U2F error.', 'two-factor' ),
202
  <p><a href="https://support.google.com/accounts/answer/6103523"><?php esc_html_e( 'You can find FIDO U2F Security Key devices for sale from here.', 'two-factor' ); ?></a></p>
203
 
204
  <?php
205
+ require( TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f-admin-list-table.php' );
206
+ $u2f_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table();
207
  $u2f_list_table->items = $security_keys;
208
  $u2f_list_table->prepare_items();
209
  $u2f_list_table->display();
232
 
233
  try {
234
  $response = json_decode( stripslashes( $_POST['u2f_response'] ) );
235
+ $reg = Two_Factor_FIDO_U2F::$u2f->doRegister( get_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY, true ), $response );
236
  $reg->new = true;
237
 
238
  Two_Factor_FIDO_U2F::add_security_key( $user_id, $reg );
242
 
243
  delete_user_meta( $user_id, self::REGISTER_DATA_USER_META_KEY );
244
 
245
+ wp_safe_redirect( add_query_arg( array(
246
+ 'new_app_pass' => 1,
247
+ ), wp_get_referer() ) . '#security-keys-section' );
 
 
 
 
 
248
  exit;
249
  }
250
  }
300
  * @return string
301
  */
302
  public static function delete_link( $item ) {
303
+ $delete_link = add_query_arg( 'delete_security_key', $item->keyHandle );
304
  $delete_link = wp_nonce_url( $delete_link, "delete_security_key-{$item->keyHandle}", '_nonce_delete_security_key' );
305
  return sprintf( '<a href="%1$s">%2$s</a>', esc_url( $delete_link ), esc_html__( 'Delete', 'two-factor' ) );
306
  }
316
  public static function wp_ajax_inline_save() {
317
  check_ajax_referer( 'keyinlineeditnonce', '_inline_edit' );
318
 
319
+ require( TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f-admin-list-table.php' );
320
  $wp_list_table = new Two_Factor_FIDO_U2F_Admin_List_Table();
321
 
322
  if ( ! isset( $_POST['keyHandle'] ) ) {
323
  wp_die();
324
  }
325
 
326
+ $user_id = Two_Factor_Core::current_user_being_edited();
327
  $security_keys = Two_Factor_FIDO_U2F::get_security_keys( $user_id );
328
  if ( ! $security_keys ) {
329
  wp_die();
330
  }
331
 
332
  foreach ( $security_keys as &$key ) {
333
+ if ( $key->keyHandle === $_POST['keyHandle'] ) {
334
  break;
335
  }
336
  }
providers/{class-two-factor-fido-u2f.php → class.two-factor-fido-u2f.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Class for creating a FIDO Universal 2nd Factor provider.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  /**
9
  * Class for creating a FIDO Universal 2nd Factor provider.
10
  *
@@ -47,7 +41,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
47
  *
48
  * @return \Two_Factor_FIDO_U2F
49
  */
50
- public static function get_instance() {
51
  static $instance;
52
 
53
  if ( ! isset( $instance ) ) {
@@ -67,18 +61,29 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
67
  return;
68
  }
69
 
70
- require_once TWO_FACTOR_DIR . 'includes/Yubico/U2F.php';
71
  self::$u2f = new u2flib_server\U2F( self::get_u2f_app_id() );
72
 
73
- require_once TWO_FACTOR_DIR . 'providers/class-two-factor-fido-u2f-admin.php';
74
  Two_Factor_FIDO_U2F_Admin::add_hooks();
75
 
76
- // Ensure the script dependencies have been registered before they're enqueued at a later priority.
77
- add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ), 5 );
78
- add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ), 5 );
79
- add_action( 'login_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ), 5 );
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_options' ) );
82
 
83
  return parent::__construct();
84
  }
@@ -116,31 +121,16 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
116
  * @since 0.1-dev
117
  */
118
  public function get_label() {
119
- return _x( 'FIDO U2F Security Keys', 'Provider Label', 'two-factor' );
120
  }
121
 
122
  /**
123
- * Register script dependencies used during login and when
124
- * registering keys in the WP admin.
125
  *
126
- * @return void
127
  */
128
- public static function enqueue_scripts() {
129
- wp_register_script(
130
- 'fido-u2f-api',
131
- plugins_url( 'includes/Google/u2f-api.js', dirname( __FILE__ ) ),
132
- null,
133
- self::asset_version(),
134
- true
135
- );
136
-
137
- wp_register_script(
138
- 'fido-u2f-login',
139
- plugins_url( 'js/fido-u2f-login.js', __FILE__ ),
140
- array( 'jquery', 'fido-u2f-api' ),
141
- self::asset_version(),
142
- true
143
- );
144
  }
145
 
146
  /**
@@ -152,7 +142,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
152
  * @return null
153
  */
154
  public function authentication_page( $user ) {
155
- require_once ABSPATH . '/wp-admin/includes/template.php';
156
 
157
  // U2F doesn't work without HTTPS.
158
  if ( ! is_ssl() ) {
@@ -208,7 +198,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
208
  try {
209
  $reg = self::$u2f->doAuthenticate( $requests, $keys, $response );
210
 
211
- $reg->last_used = time();
212
 
213
  self::update_security_key( $user->ID, $reg );
214
 
@@ -261,8 +251,8 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
261
 
262
  if (
263
  ! is_object( $register )
264
- || ! property_exists( $register, 'keyHandle' ) || empty( $register->keyHandle ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
265
- || ! property_exists( $register, 'publicKey' ) || empty( $register->publicKey ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
266
  || ! property_exists( $register, 'certificate' ) || empty( $register->certificate )
267
  || ! property_exists( $register, 'counter' ) || ( -1 > $register->counter )
268
  ) {
@@ -270,14 +260,14 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
270
  }
271
 
272
  $register = array(
273
- 'keyHandle' => $register->keyHandle, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
274
- 'publicKey' => $register->publicKey, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
275
  'certificate' => $register->certificate,
276
  'counter' => $register->counter,
277
  );
278
 
279
  $register['name'] = __( 'New Security Key', 'two-factor' );
280
- $register['added'] = time();
281
  $register['last_used'] = $register['added'];
282
 
283
  return add_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, $register );
@@ -328,8 +318,8 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
328
 
329
  if (
330
  ! is_object( $data )
331
- || ! property_exists( $data, 'keyHandle' ) || empty( $data->keyHandle ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
332
- || ! property_exists( $data, 'publicKey' ) || empty( $data->publicKey ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
333
  || ! property_exists( $data, 'certificate' ) || empty( $data->certificate )
334
  || ! property_exists( $data, 'counter' ) || ( -1 > $data->counter )
335
  ) {
@@ -339,7 +329,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
339
  $keys = self::get_security_keys( $user_id );
340
  if ( $keys ) {
341
  foreach ( $keys as $key ) {
342
- if ( $key->keyHandle === $data->keyHandle ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
343
  return update_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, (array) $data, (array) $key );
344
  }
345
  }
@@ -357,7 +347,7 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
357
  * @param string $keyHandle Optional. Key handle.
358
  * @return bool True on success, false on failure.
359
  */
360
- public static function delete_security_key( $user_id, $keyHandle = null ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
361
  global $wpdb;
362
 
363
  if ( ! is_numeric( $user_id ) ) {
@@ -369,21 +359,18 @@ class Two_Factor_FIDO_U2F extends Two_Factor_Provider {
369
  return false;
370
  }
371
 
372
- $keyHandle = wp_unslash( $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
373
- $keyHandle = maybe_serialize( $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
374
 
375
- $query = $wpdb->prepare( "SELECT umeta_id FROM {$wpdb->usermeta} WHERE meta_key = %s AND user_id = %d", self::REGISTERED_KEY_USER_META_KEY, $user_id );
 
376
 
377
- if ( $keyHandle ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
378
- $key_handle_lookup = sprintf( ':"%s";s:', $keyHandle ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
379
 
380
- $query .= $wpdb->prepare(
381
- ' AND meta_value LIKE %s',
382
- '%' . $wpdb->esc_like( $key_handle_lookup ) . '%'
383
- );
384
  }
385
 
386
- $meta_ids = $wpdb->get_col( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
387
  if ( ! count( $meta_ids ) ) {
388
  return false;
389
  }
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Class for creating a FIDO Universal 2nd Factor provider.
4
  *
41
  *
42
  * @return \Two_Factor_FIDO_U2F
43
  */
44
+ static function get_instance() {
45
  static $instance;
46
 
47
  if ( ! isset( $instance ) ) {
61
  return;
62
  }
63
 
64
+ require_once( TWO_FACTOR_DIR . 'includes/Yubico/U2F.php' );
65
  self::$u2f = new u2flib_server\U2F( self::get_u2f_app_id() );
66
 
67
+ require_once( TWO_FACTOR_DIR . 'providers/class.two-factor-fido-u2f-admin.php' );
68
  Two_Factor_FIDO_U2F_Admin::add_hooks();
69
 
70
+ wp_register_script(
71
+ 'fido-u2f-api',
72
+ plugins_url( 'includes/Google/u2f-api.js', dirname( __FILE__ ) ),
73
+ null,
74
+ self::asset_version(),
75
+ true
76
+ );
77
+
78
+ wp_register_script(
79
+ 'fido-u2f-login',
80
+ plugins_url( 'js/fido-u2f-login.js', __FILE__ ),
81
+ array( 'jquery', 'fido-u2f-api' ),
82
+ self::asset_version(),
83
+ true
84
+ );
85
 
86
+ add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_options' ) );
87
 
88
  return parent::__construct();
89
  }
121
  * @since 0.1-dev
122
  */
123
  public function get_label() {
124
+ return _x( 'FIDO Universal 2nd Factor (U2F)', 'Provider Label', 'two-factor' );
125
  }
126
 
127
  /**
128
+ * Enqueue assets for login form.
 
129
  *
130
+ * @since 0.1-dev
131
  */
132
+ public function login_enqueue_assets() {
133
+ wp_enqueue_script( 'fido-u2f-login' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  }
135
 
136
  /**
142
  * @return null
143
  */
144
  public function authentication_page( $user ) {
145
+ require_once( ABSPATH . '/wp-admin/includes/template.php' );
146
 
147
  // U2F doesn't work without HTTPS.
148
  if ( ! is_ssl() ) {
198
  try {
199
  $reg = self::$u2f->doAuthenticate( $requests, $keys, $response );
200
 
201
+ $reg->last_used = current_time( 'timestamp' );
202
 
203
  self::update_security_key( $user->ID, $reg );
204
 
251
 
252
  if (
253
  ! is_object( $register )
254
+ || ! property_exists( $register, 'keyHandle' ) || empty( $register->keyHandle )
255
+ || ! property_exists( $register, 'publicKey' ) || empty( $register->publicKey )
256
  || ! property_exists( $register, 'certificate' ) || empty( $register->certificate )
257
  || ! property_exists( $register, 'counter' ) || ( -1 > $register->counter )
258
  ) {
260
  }
261
 
262
  $register = array(
263
+ 'keyHandle' => $register->keyHandle,
264
+ 'publicKey' => $register->publicKey,
265
  'certificate' => $register->certificate,
266
  'counter' => $register->counter,
267
  );
268
 
269
  $register['name'] = __( 'New Security Key', 'two-factor' );
270
+ $register['added'] = current_time( 'timestamp' );
271
  $register['last_used'] = $register['added'];
272
 
273
  return add_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, $register );
318
 
319
  if (
320
  ! is_object( $data )
321
+ || ! property_exists( $data, 'keyHandle' ) || empty( $data->keyHandle )
322
+ || ! property_exists( $data, 'publicKey' ) || empty( $data->publicKey )
323
  || ! property_exists( $data, 'certificate' ) || empty( $data->certificate )
324
  || ! property_exists( $data, 'counter' ) || ( -1 > $data->counter )
325
  ) {
329
  $keys = self::get_security_keys( $user_id );
330
  if ( $keys ) {
331
  foreach ( $keys as $key ) {
332
+ if ( $key->keyHandle === $data->keyHandle ) {
333
  return update_user_meta( $user_id, self::REGISTERED_KEY_USER_META_KEY, (array) $data, (array) $key );
334
  }
335
  }
347
  * @param string $keyHandle Optional. Key handle.
348
  * @return bool True on success, false on failure.
349
  */
350
+ public static function delete_security_key( $user_id, $keyHandle = null ) {
351
  global $wpdb;
352
 
353
  if ( ! is_numeric( $user_id ) ) {
359
  return false;
360
  }
361
 
362
+ $table = $wpdb->usermeta;
 
363
 
364
+ $keyHandle = wp_unslash( $keyHandle );
365
+ $keyHandle = maybe_serialize( $keyHandle );
366
 
367
+ $query = $wpdb->prepare( "SELECT umeta_id FROM $table WHERE meta_key = '%s' AND user_id = %d", self::REGISTERED_KEY_USER_META_KEY, $user_id );
 
368
 
369
+ if ( $keyHandle ) {
370
+ $query .= $wpdb->prepare( ' AND meta_value LIKE %s', '%:"' . $keyHandle . '";s:%' );
 
 
371
  }
372
 
373
+ $meta_ids = $wpdb->get_col( $query );
374
  if ( ! count( $meta_ids ) ) {
375
  return false;
376
  }
providers/{class-two-factor-provider.php → class.two-factor-provider.php} RENAMED
@@ -1,10 +1,4 @@
1
  <?php
2
- /**
3
- * Abstract class for creating two factor authentication providers.
4
- *
5
- * @package Two_Factor
6
- */
7
-
8
  /**
9
  * Abstract class for creating two factor authentication providers.
10
  *
@@ -30,7 +24,7 @@ abstract class Two_Factor_Provider {
30
  *
31
  * @return string
32
  */
33
- abstract public function get_label();
34
 
35
  /**
36
  * Prints the name of the provider.
@@ -48,7 +42,7 @@ abstract class Two_Factor_Provider {
48
  *
49
  * @param WP_User $user WP_User object of the logged-in user.
50
  */
51
- abstract public function authentication_page( $user );
52
 
53
  /**
54
  * Allow providers to do extra processing before the authentication.
@@ -70,7 +64,7 @@ abstract class Two_Factor_Provider {
70
  * @param WP_User $user WP_User object of the logged-in user.
71
  * @return boolean
72
  */
73
- abstract public function validate_authentication( $user );
74
 
75
  /**
76
  * Whether this Two Factor provider is configured and available for the user specified.
@@ -78,7 +72,7 @@ abstract class Two_Factor_Provider {
78
  * @param WP_User $user WP_User object of the logged-in user.
79
  * @return boolean
80
  */
81
- abstract public function is_available_for_user( $user );
82
 
83
  /**
84
  * Generate a random eight-digit string to send out as an auth code.
1
  <?php
 
 
 
 
 
 
2
  /**
3
  * Abstract class for creating two factor authentication providers.
4
  *
24
  *
25
  * @return string
26
  */
27
+ abstract function get_label();
28
 
29
  /**
30
  * Prints the name of the provider.
42
  *
43
  * @param WP_User $user WP_User object of the logged-in user.
44
  */
45
+ abstract function authentication_page( $user );
46
 
47
  /**
48
  * Allow providers to do extra processing before the authentication.
64
  * @param WP_User $user WP_User object of the logged-in user.
65
  * @return boolean
66
  */
67
+ abstract function validate_authentication( $user );
68
 
69
  /**
70
  * Whether this Two Factor provider is configured and available for the user specified.
72
  * @param WP_User $user WP_User object of the logged-in user.
73
  * @return boolean
74
  */
75
+ abstract function is_available_for_user( $user );
76
 
77
  /**
78
  * Generate a random eight-digit string to send out as an auth code.
providers/{class-two-factor-totp.php → class.two-factor-totp.php} RENAMED
@@ -31,24 +31,18 @@ class Two_Factor_Totp extends Two_Factor_Provider {
31
  */
32
  const ACTION_SECRET_DELETE = 'totp-delete';
33
 
34
- const DEFAULT_KEY_BIT_SIZE = 160;
35
- const DEFAULT_CRYPTO = 'sha1';
36
- const DEFAULT_DIGIT_COUNT = 6;
37
- const DEFAULT_TIME_STEP_SEC = 30;
38
  const DEFAULT_TIME_STEP_ALLOWANCE = 4;
39
-
40
- /**
41
- * Chracters used in base32 encoding.
42
- *
43
- * @var string
44
- */
45
- private static $base_32_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
46
 
47
  /**
48
  * Class constructor. Sets up hooks, etc.
49
  */
50
  protected function __construct() {
51
- add_action( 'two_factor_user_options_' . __CLASS__, array( $this, 'user_two_factor_options' ) );
52
  add_action( 'personal_options_update', array( $this, 'user_two_factor_options_update' ) );
53
  add_action( 'edit_user_profile_update', array( $this, 'user_two_factor_options_update' ) );
54
  add_action( 'two_factor_user_settings_action', array( $this, 'user_settings_action' ), 10, 2 );
@@ -71,7 +65,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
71
  * Returns the name of the provider.
72
  */
73
  public function get_label() {
74
- return _x( 'Time Based One-Time Password (TOTP)', 'Provider Label', 'two-factor' );
75
  }
76
 
77
  /**
@@ -117,10 +111,9 @@ class Two_Factor_Totp extends Two_Factor_Provider {
117
 
118
  ?>
119
  <div id="two-factor-totp-options">
120
- <?php
121
- if ( empty( $key ) ) :
122
- $key = $this->generate_key();
123
- $site_name = get_bloginfo( 'name', 'display' );
124
  $totp_title = apply_filters( 'two_factor_totp_title', $site_name . ':' . $user->user_login, $user );
125
  ?>
126
  <p>
@@ -142,7 +135,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
142
  </p>
143
  <?php else : ?>
144
  <p class="success">
145
- <?php esc_html_e( 'Secret key is configured and registered. It is not possible to view it again for security reasons.', 'two-factor' ); ?>
146
  </p>
147
  <p>
148
  <a class="button" href="<?php echo esc_url( self::get_token_delete_url_for_user( $user->ID ) ); ?>"><?php esc_html_e( 'Reset Key', 'two-factor' ); ?></a>
@@ -159,12 +152,11 @@ class Two_Factor_Totp extends Two_Factor_Provider {
159
  * Save the options specified in `::user_two_factor_options()`
160
  *
161
  * @param integer $user_id The user ID whose options are being updated.
162
- *
163
- * @return void
164
  */
165
  public function user_two_factor_options_update( $user_id ) {
166
  $notices = array();
167
- $errors = array();
168
 
169
  if ( isset( $_POST['_nonce_user_two_factor_totp_options'] ) ) {
170
  check_admin_referer( 'user_two_factor_totp_options', '_nonce_user_two_factor_totp_options' );
@@ -172,8 +164,8 @@ class Two_Factor_Totp extends Two_Factor_Provider {
172
  // Validate and store a new secret key.
173
  if ( ! empty( $_POST['two-factor-totp-authcode'] ) && ! empty( $_POST['two-factor-totp-key'] ) ) {
174
  // Don't use filter_input() because we can't mock it during tests for now.
175
- $authcode = filter_var( sanitize_text_field( $_POST['two-factor-totp-authcode'] ), FILTER_SANITIZE_NUMBER_INT );
176
- $key = sanitize_text_field( $_POST['two-factor-totp-key'] );
177
 
178
  if ( $this->is_valid_key( $key ) ) {
179
  if ( $this->is_valid_authcode( $key, $authcode ) ) {
@@ -240,7 +232,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
240
  * @return boolean
241
  */
242
  public function is_valid_key( $key ) {
243
- $check = sprintf( '/^[%s]+$/', self::$base_32_chars );
244
 
245
  if ( 1 === preg_match( $check, $key ) ) {
246
  return true;
@@ -264,7 +256,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
264
 
265
  foreach ( $notices as $class => $messages ) {
266
  ?>
267
- <div class="<?php echo esc_attr( $class ); ?>">
268
  <?php
269
  foreach ( $messages as $msg ) {
270
  ?>
@@ -288,10 +280,10 @@ class Two_Factor_Totp extends Two_Factor_Provider {
288
  * @return bool Whether the user gave a valid code
289
  */
290
  public function validate_authentication( $user ) {
291
- if ( ! empty( $_REQUEST['authcode'] ) ) {
292
  return $this->is_valid_authcode(
293
  $this->get_user_totp_key( $user->ID ),
294
- sanitize_text_field( $_REQUEST['authcode'] )
295
  );
296
  }
297
 
@@ -313,12 +305,9 @@ class Two_Factor_Totp extends Two_Factor_Provider {
313
  * Ticks are the allowed offset from the correct time in 30 second increments,
314
  * so the default of 4 allows codes that are two minutes to either side of server time
315
  *
316
- * @deprecated 0.7.0 Use {@see 'two_factor_totp_time_step_allowance'} instead.
317
  * @param int $max_ticks Max ticks of time correction to allow. Default 4.
318
  */
319
- $max_ticks = apply_filters_deprecated( 'two-factor-totp-time-step-allowance', array( self::DEFAULT_TIME_STEP_ALLOWANCE ), '0.7.0', 'two_factor_totp_time_step_allowance' );
320
-
321
- $max_ticks = apply_filters( 'two_factor_totp_time_step_allowance', self::DEFAULT_TIME_STEP_ALLOWANCE );
322
 
323
  // Array of all ticks to allow, sorted using absolute value to test closest match first.
324
  $ticks = range( - $max_ticks, $max_ticks );
@@ -343,7 +332,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
343
  * @return string $bitsize long string composed of available base32 chars.
344
  */
345
  public static function generate_key( $bitsize = self::DEFAULT_KEY_BIT_SIZE ) {
346
- $bytes = ceil( $bitsize / 8 );
347
  $secret = wp_generate_password( $bytes, true, true );
348
 
349
  return self::base32_encode( $secret );
@@ -373,8 +362,8 @@ class Two_Factor_Totp extends Two_Factor_Provider {
373
  $higher = 0;
374
  }
375
 
376
- $lowmap = 0xffffffff;
377
- $lower = $value & $lowmap;
378
 
379
  return pack( 'NN', $higher, $lower );
380
  }
@@ -424,7 +413,7 @@ class Two_Factor_Totp extends Two_Factor_Provider {
424
  */
425
  public static function get_google_qr_code( $name, $key, $title = null ) {
426
  // Encode to support spaces, question marks and other characters.
427
- $name = rawurlencode( $name );
428
  $google_url = urlencode( 'otpauth://totp/' . $name . '?secret=' . $key );
429
  if ( isset( $title ) ) {
430
  $google_url .= urlencode( '&issuer=' . rawurlencode( $title ) );
@@ -452,20 +441,18 @@ class Two_Factor_Totp extends Two_Factor_Provider {
452
  * @param WP_User $user WP_User object of the logged-in user.
453
  */
454
  public function authentication_page( $user ) {
455
- require_once ABSPATH . '/wp-admin/includes/template.php';
456
  ?>
457
- <p>
458
- <?php esc_html_e( 'Please enter the code generated by your authenticator app.', 'two-factor' ); ?>
459
- </p>
460
  <p>
461
  <label for="authcode"><?php esc_html_e( 'Authentication Code:', 'two-factor' ); ?></label>
462
- <input type="tel" autocomplete="off" name="authcode" id="authcode" class="input" value="" size="20" pattern="[0-9]*" />
463
  </p>
464
  <script type="text/javascript">
465
  setTimeout( function(){
466
  var d;
467
  try{
468
  d = document.getElementById('authcode');
 
469
  d.focus();
470
  } catch(e){}
471
  }, 200);
@@ -493,10 +480,10 @@ class Two_Factor_Totp extends Two_Factor_Provider {
493
  }
494
 
495
  $five_bit_sections = str_split( $binary_string, 5 );
496
- $base32_string = '';
497
 
498
  foreach ( $five_bit_sections as $five_bit_section ) {
499
- $base32_string .= self::$base_32_chars[ base_convert( str_pad( $five_bit_section, 5, '0' ), 2, 10 ) ];
500
  }
501
 
502
  return $base32_string;
@@ -513,25 +500,25 @@ class Two_Factor_Totp extends Two_Factor_Provider {
513
  */
514
  public static function base32_decode( $base32_string ) {
515
 
516
- $base32_string = strtoupper( $base32_string );
517
 
518
- if ( ! preg_match( '/^[' . self::$base_32_chars . ']+$/', $base32_string, $match ) ) {
519
  throw new Exception( 'Invalid characters in the base32 string.' );
520
  }
521
 
522
- $l = strlen( $base32_string );
523
- $n = 0;
524
- $j = 0;
525
  $binary = '';
526
 
527
  for ( $i = 0; $i < $l; $i++ ) {
528
 
529
- $n = $n << 5; // Move buffer left by 5 to make room.
530
- $n = $n + strpos( self::$base_32_chars, $base32_string[ $i ] ); // Add value into buffer.
531
  $j += 5; // Keep track of number of bits in buffer.
532
 
533
  if ( $j >= 8 ) {
534
- $j -= 8;
535
  $binary .= chr( ( $n & ( 0xFF << $j ) ) >> $j );
536
  }
537
  }
@@ -553,6 +540,6 @@ class Two_Factor_Totp extends Two_Factor_Provider {
553
  if ( $a === $b ) {
554
  return 0;
555
  }
556
- return ( $a < $b ) ? -1 : 1;
557
  }
558
  }
31
  */
32
  const ACTION_SECRET_DELETE = 'totp-delete';
33
 
34
+ const DEFAULT_KEY_BIT_SIZE = 160;
35
+ const DEFAULT_CRYPTO = 'sha1';
36
+ const DEFAULT_DIGIT_COUNT = 6;
37
+ const DEFAULT_TIME_STEP_SEC = 30;
38
  const DEFAULT_TIME_STEP_ALLOWANCE = 4;
39
+ private static $_base_32_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
 
 
 
 
 
 
40
 
41
  /**
42
  * Class constructor. Sets up hooks, etc.
43
  */
44
  protected function __construct() {
45
+ add_action( 'two-factor-user-options-' . __CLASS__, array( $this, 'user_two_factor_options' ) );
46
  add_action( 'personal_options_update', array( $this, 'user_two_factor_options_update' ) );
47
  add_action( 'edit_user_profile_update', array( $this, 'user_two_factor_options_update' ) );
48
  add_action( 'two_factor_user_settings_action', array( $this, 'user_settings_action' ), 10, 2 );
65
  * Returns the name of the provider.
66
  */
67
  public function get_label() {
68
+ return _x( 'Time Based One-Time Password (Google Authenticator)', 'Provider Label', 'two-factor' );
69
  }
70
 
71
  /**
111
 
112
  ?>
113
  <div id="two-factor-totp-options">
114
+ <?php if ( empty( $key ) ) :
115
+ $key = $this->generate_key();
116
+ $site_name = get_bloginfo( 'name', 'display' );
 
117
  $totp_title = apply_filters( 'two_factor_totp_title', $site_name . ':' . $user->user_login, $user );
118
  ?>
119
  <p>
135
  </p>
136
  <?php else : ?>
137
  <p class="success">
138
+ <?php esc_html_e( 'Secret key configured and registered.', 'two-factor' ); ?>
139
  </p>
140
  <p>
141
  <a class="button" href="<?php echo esc_url( self::get_token_delete_url_for_user( $user->ID ) ); ?>"><?php esc_html_e( 'Reset Key', 'two-factor' ); ?></a>
152
  * Save the options specified in `::user_two_factor_options()`
153
  *
154
  * @param integer $user_id The user ID whose options are being updated.
155
+ * @return false
 
156
  */
157
  public function user_two_factor_options_update( $user_id ) {
158
  $notices = array();
159
+ $errors = array();
160
 
161
  if ( isset( $_POST['_nonce_user_two_factor_totp_options'] ) ) {
162
  check_admin_referer( 'user_two_factor_totp_options', '_nonce_user_two_factor_totp_options' );
164
  // Validate and store a new secret key.
165
  if ( ! empty( $_POST['two-factor-totp-authcode'] ) && ! empty( $_POST['two-factor-totp-key'] ) ) {
166
  // Don't use filter_input() because we can't mock it during tests for now.
167
+ $authcode = sanitize_text_field( $_POST['two-factor-totp-authcode'] );
168
+ $key = sanitize_text_field( $_POST['two-factor-totp-key'] );
169
 
170
  if ( $this->is_valid_key( $key ) ) {
171
  if ( $this->is_valid_authcode( $key, $authcode ) ) {
232
  * @return boolean
233
  */
234
  public function is_valid_key( $key ) {
235
+ $check = sprintf( '/^[%s]+$/', self::$_base_32_chars );
236
 
237
  if ( 1 === preg_match( $check, $key ) ) {
238
  return true;
256
 
257
  foreach ( $notices as $class => $messages ) {
258
  ?>
259
+ <div class="<?php echo esc_attr( $class ) ?>">
260
  <?php
261
  foreach ( $messages as $msg ) {
262
  ?>
280
  * @return bool Whether the user gave a valid code
281
  */
282
  public function validate_authentication( $user ) {
283
+ if ( ! empty( $_REQUEST['authcode'] ) ) { // WPCS: CSRF ok, nonce verified by login_form_validate_2fa().
284
  return $this->is_valid_authcode(
285
  $this->get_user_totp_key( $user->ID ),
286
+ sanitize_text_field( $_REQUEST['authcode'] ) // WPCS: CSRF ok, nonce verified by login_form_validate_2fa().
287
  );
288
  }
289
 
305
  * Ticks are the allowed offset from the correct time in 30 second increments,
306
  * so the default of 4 allows codes that are two minutes to either side of server time
307
  *
 
308
  * @param int $max_ticks Max ticks of time correction to allow. Default 4.
309
  */
310
+ $max_ticks = apply_filters( 'two-factor-totp-time-step-allowance', self::DEFAULT_TIME_STEP_ALLOWANCE );
 
 
311
 
312
  // Array of all ticks to allow, sorted using absolute value to test closest match first.
313
  $ticks = range( - $max_ticks, $max_ticks );
332
  * @return string $bitsize long string composed of available base32 chars.
333
  */
334
  public static function generate_key( $bitsize = self::DEFAULT_KEY_BIT_SIZE ) {
335
+ $bytes = ceil( $bitsize / 8 );
336
  $secret = wp_generate_password( $bytes, true, true );
337
 
338
  return self::base32_encode( $secret );
362
  $higher = 0;
363
  }
364
 
365
+ $lowmap = 0xffffffff;
366
+ $lower = $value & $lowmap;
367
 
368
  return pack( 'NN', $higher, $lower );
369
  }
413
  */
414
  public static function get_google_qr_code( $name, $key, $title = null ) {
415
  // Encode to support spaces, question marks and other characters.
416
+ $name = rawurlencode( $name );
417
  $google_url = urlencode( 'otpauth://totp/' . $name . '?secret=' . $key );
418
  if ( isset( $title ) ) {
419
  $google_url .= urlencode( '&issuer=' . rawurlencode( $title ) );
441
  * @param WP_User $user WP_User object of the logged-in user.
442
  */
443
  public function authentication_page( $user ) {
444
+ require_once( ABSPATH . '/wp-admin/includes/template.php' );
445
  ?>
 
 
 
446
  <p>
447
  <label for="authcode"><?php esc_html_e( 'Authentication Code:', 'two-factor' ); ?></label>
448
+ <input type="tel" name="authcode" id="authcode" class="input" value="" size="20" pattern="[0-9]*" />
449
  </p>
450
  <script type="text/javascript">
451
  setTimeout( function(){
452
  var d;
453
  try{
454
  d = document.getElementById('authcode');
455
+ d.value = '';
456
  d.focus();
457
  } catch(e){}
458
  }, 200);
480
  }
481
 
482
  $five_bit_sections = str_split( $binary_string, 5 );
483
+ $base32_string = '';
484
 
485
  foreach ( $five_bit_sections as $five_bit_section ) {
486
+ $base32_string .= self::$_base_32_chars[ base_convert( str_pad( $five_bit_section, 5, '0' ), 2, 10 ) ];
487
  }
488
 
489
  return $base32_string;
500
  */
501
  public static function base32_decode( $base32_string ) {
502
 
503
+ $base32_string = strtoupper( $base32_string );
504
 
505
+ if ( ! preg_match( '/^[' . self::$_base_32_chars . ']+$/', $base32_string, $match ) ) {
506
  throw new Exception( 'Invalid characters in the base32 string.' );
507
  }
508
 
509
+ $l = strlen( $base32_string );
510
+ $n = 0;
511
+ $j = 0;
512
  $binary = '';
513
 
514
  for ( $i = 0; $i < $l; $i++ ) {
515
 
516
+ $n = $n << 5; // Move buffer left by 5 to make room.
517
+ $n = $n + strpos( self::$_base_32_chars, $base32_string[ $i ] ); // Add value into buffer.
518
  $j += 5; // Keep track of number of bits in buffer.
519
 
520
  if ( $j >= 8 ) {
521
+ $j -= 8;
522
  $binary .= chr( ( $n & ( 0xFF << $j ) ) >> $j );
523
  }
524
  }
540
  if ( $a === $b ) {
541
  return 0;
542
  }
543
+ return ($a < $b) ? -1 : 1;
544
  }
545
  }
readme.md CHANGED
@@ -7,7 +7,7 @@ Enable Two-Factor Authentication using time-based one-time passwords (OTP, Googl
7
  **Contributors:** [georgestephanis](https://profiles.wordpress.org/georgestephanis), [valendesigns](https://profiles.wordpress.org/valendesigns), [stevenkword](https://profiles.wordpress.org/stevenkword), [extendwings](https://profiles.wordpress.org/extendwings), [sgrant](https://profiles.wordpress.org/sgrant), [aaroncampbell](https://profiles.wordpress.org/aaroncampbell), [johnbillion](https://profiles.wordpress.org/johnbillion), [stevegrunwell](https://profiles.wordpress.org/stevegrunwell), [netweb](https://profiles.wordpress.org/netweb), [kasparsd](https://profiles.wordpress.org/kasparsd), [alihusnainarshad](https://profiles.wordpress.org/alihusnainarshad), [passoniate](https://profiles.wordpress.org/passoniate)
8
  **Tags:** [two factor](https://wordpress.org/plugins/tags/two-factor), [two step](https://wordpress.org/plugins/tags/two-step), [authentication](https://wordpress.org/plugins/tags/authentication), [login](https://wordpress.org/plugins/tags/login), [totp](https://wordpress.org/plugins/tags/totp), [fido u2f](https://wordpress.org/plugins/tags/fido-u2f), [u2f](https://wordpress.org/plugins/tags/u2f), [email](https://wordpress.org/plugins/tags/email), [backup codes](https://wordpress.org/plugins/tags/backup-codes), [2fa](https://wordpress.org/plugins/tags/2fa), [yubikey](https://wordpress.org/plugins/tags/yubikey)
9
  **Requires at least:** 4.3
10
- **Tested up to:** 5.5
11
  **Stable tag:** trunk (master)
12
  **Requires PHP:** 5.6
13
 
@@ -27,10 +27,7 @@ For more history, see [this post](https://georgestephanis.wordpress.com/2013/08/
27
  ### Actions & Filters ###
28
  Here is a list of action and filter hooks provided by the plugin:
29
 
30
- - `two_factor_providers` filter overrides the available two-factor providers such as email and time-based one-time passwords. Array values are PHP classnames of the two-factor providers.
31
- - `two_factor_enabled_providers_for_user` filter overrides the list of two-factor providers enabled for a user. First argument is an array of enabled provider classnames as values, the second argument is the user ID.
32
- - `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
33
- - `two_factor_token_ttl` filter overrides the time interval in seconds that an email token is considered after generation. Accepts the time in seconds as the first argument and the ID of the `WP_User` object being authenticated.
34
 
35
 
36
  ## Screenshots ##
7
  **Contributors:** [georgestephanis](https://profiles.wordpress.org/georgestephanis), [valendesigns](https://profiles.wordpress.org/valendesigns), [stevenkword](https://profiles.wordpress.org/stevenkword), [extendwings](https://profiles.wordpress.org/extendwings), [sgrant](https://profiles.wordpress.org/sgrant), [aaroncampbell](https://profiles.wordpress.org/aaroncampbell), [johnbillion](https://profiles.wordpress.org/johnbillion), [stevegrunwell](https://profiles.wordpress.org/stevegrunwell), [netweb](https://profiles.wordpress.org/netweb), [kasparsd](https://profiles.wordpress.org/kasparsd), [alihusnainarshad](https://profiles.wordpress.org/alihusnainarshad), [passoniate](https://profiles.wordpress.org/passoniate)
8
  **Tags:** [two factor](https://wordpress.org/plugins/tags/two-factor), [two step](https://wordpress.org/plugins/tags/two-step), [authentication](https://wordpress.org/plugins/tags/authentication), [login](https://wordpress.org/plugins/tags/login), [totp](https://wordpress.org/plugins/tags/totp), [fido u2f](https://wordpress.org/plugins/tags/fido-u2f), [u2f](https://wordpress.org/plugins/tags/u2f), [email](https://wordpress.org/plugins/tags/email), [backup codes](https://wordpress.org/plugins/tags/backup-codes), [2fa](https://wordpress.org/plugins/tags/2fa), [yubikey](https://wordpress.org/plugins/tags/yubikey)
9
  **Requires at least:** 4.3
10
+ **Tested up to:** 5.4
11
  **Stable tag:** trunk (master)
12
  **Requires PHP:** 5.6
13
 
27
  ### Actions & Filters ###
28
  Here is a list of action and filter hooks provided by the plugin:
29
 
30
+ - `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
 
 
 
31
 
32
 
33
  ## Screenshots ##
readme.txt CHANGED
@@ -2,7 +2,7 @@
2
  Contributors: georgestephanis, valendesigns, stevenkword, extendwings, sgrant, aaroncampbell, johnbillion, stevegrunwell, netweb, kasparsd, alihusnainarshad, passoniate
3
  Tags: two factor, two step, authentication, login, totp, fido u2f, u2f, email, backup codes, 2fa, yubikey
4
  Requires at least: 4.3
5
- Tested up to: 5.5
6
  Requires PHP: 5.6
7
  Stable tag: trunk
8
 
@@ -24,10 +24,7 @@ For more history, see [this post](https://georgestephanis.wordpress.com/2013/08/
24
 
25
  Here is a list of action and filter hooks provided by the plugin:
26
 
27
- - `two_factor_providers` filter overrides the available two-factor providers such as email and time-based one-time passwords. Array values are PHP classnames of the two-factor providers.
28
- - `two_factor_enabled_providers_for_user` filter overrides the list of two-factor providers enabled for a user. First argument is an array of enabled provider classnames as values, the second argument is the user ID.
29
  - `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
30
- - `two_factor_token_ttl` filter overrides the time interval in seconds that an email token is considered after generation. Accepts the time in seconds as the first argument and the ID of the `WP_User` object being authenticated.
31
 
32
  == Screenshots ==
33
 
2
  Contributors: georgestephanis, valendesigns, stevenkword, extendwings, sgrant, aaroncampbell, johnbillion, stevegrunwell, netweb, kasparsd, alihusnainarshad, passoniate
3
  Tags: two factor, two step, authentication, login, totp, fido u2f, u2f, email, backup codes, 2fa, yubikey
4
  Requires at least: 4.3
5
+ Tested up to: 5.4
6
  Requires PHP: 5.6
7
  Stable tag: trunk
8
 
24
 
25
  Here is a list of action and filter hooks provided by the plugin:
26
 
 
 
27
  - `two_factor_user_authenticated` action which receives the logged in `WP_User` object as the first argument for determining the logged in user right after the authentication workflow.
 
28
 
29
  == Screenshots ==
30
 
two-factor.php CHANGED
@@ -1,18 +1,10 @@
1
  <?php
2
  /**
3
- * Two Factor
4
- *
5
- * @package Two_Factor
6
- * @author Plugin Contributors
7
- * @copyright 2020 Plugin Contributors
8
- * @license GPL-2.0-or-later
9
- *
10
- * @wordpress-plugin
11
  * Plugin Name: Two Factor
12
  * Plugin URI: https://wordpress.org/plugins/two-factor/
13
  * Description: Two-Factor Authentication using time-based one-time passwords, Universal 2nd Factor (FIDO U2F), email and backup verification codes.
14
  * Author: Plugin Contributors
15
- * Version: 0.7.0
16
  * Author URI: https://github.com/wordpress/two-factor/graphs/contributors
17
  * Network: True
18
  * Text Domain: two-factor
@@ -23,25 +15,20 @@
23
  */
24
  define( 'TWO_FACTOR_DIR', plugin_dir_path( __FILE__ ) );
25
 
26
- /**
27
- * Version of the plugin.
28
- */
29
- define( 'TWO_FACTOR_VERSION', '0.7.0' );
30
-
31
  /**
32
  * Include the base class here, so that other plugins can also extend it.
33
  */
34
- require_once TWO_FACTOR_DIR . 'providers/class-two-factor-provider.php';
35
 
36
  /**
37
  * Include the core that handles the common bits.
38
  */
39
- require_once TWO_FACTOR_DIR . 'class-two-factor-core.php';
40
 
41
  /**
42
  * A compatability layer for some of the most-used plugins out there.
43
  */
44
- require_once TWO_FACTOR_DIR . 'class-two-factor-compat.php';
45
 
46
  $two_factor_compat = new Two_Factor_Compat();
47
 
1
  <?php
2
  /**
 
 
 
 
 
 
 
 
3
  * Plugin Name: Two Factor
4
  * Plugin URI: https://wordpress.org/plugins/two-factor/
5
  * Description: Two-Factor Authentication using time-based one-time passwords, Universal 2nd Factor (FIDO U2F), email and backup verification codes.
6
  * Author: Plugin Contributors
7
+ * Version: 0.5.2
8
  * Author URI: https://github.com/wordpress/two-factor/graphs/contributors
9
  * Network: True
10
  * Text Domain: two-factor
15
  */
16
  define( 'TWO_FACTOR_DIR', plugin_dir_path( __FILE__ ) );
17
 
 
 
 
 
 
18
  /**
19
  * Include the base class here, so that other plugins can also extend it.
20
  */
21
+ require_once( TWO_FACTOR_DIR . 'providers/class.two-factor-provider.php' );
22
 
23
  /**
24
  * Include the core that handles the common bits.
25
  */
26
+ require_once( TWO_FACTOR_DIR . 'class-two-factor-core.php' );
27
 
28
  /**
29
  * A compatability layer for some of the most-used plugins out there.
30
  */
31
+ require_once( TWO_FACTOR_DIR . 'class-two-factor-compat.php' );
32
 
33
  $two_factor_compat = new Two_Factor_Compat();
34