PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 1.2.0
WP 2FA – Two-factor authentication for WordPress v1.2.0
1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Authenticator / BackupCodes.php
wp-2fa / includes / classes / Authenticator Last commit date
Authentication.php 6 years ago BackupCodes.php 6 years ago Login.php 6 years ago
BackupCodes.php
165 lines
1 <?php // phpcs:ignore
2 /**
3 * Class for handling backup codes
4 *
5 * @since 0.1-dev
6 *
7 * @package WP2FA
8 */
9
10 namespace WP2FA\Authenticator;
11
12 use \WP2FA\Authenticator\Authentication as Authentication;
13
14 /**
15 * Backup code class, for handling backup code generation and such.
16 */
17 class BackupCodes {
18
19 /**
20 * Key used for backup codes
21 *
22 * @var string
23 */
24 const BACKUP_CODES_META_KEY = 'wp_2fa_backup_codes';
25
26 /**
27 * The number backup codes.
28 *
29 * @type int
30 */
31 const NUMBER_OF_CODES = 10;
32
33 /**
34 * Lets build!
35 */
36 public function __construct() {
37 add_action( 'wp_ajax_run_ajax_generate_json', array( $this, 'run_ajax_generate_json' ) );
38 }
39
40 /**
41 * Generate backup codes
42 *
43 * @param object $user User data.
44 * @param string $args possible args.
45 */
46 public static function generate_codes( $user, $args = '' ) {
47 $codes = array();
48 $codes_hashed = array();
49
50 // Check for arguments.
51 if ( isset( $args['number'] ) ) {
52 $num_codes = (int) $args['number'];
53 } else {
54 $num_codes = self::NUMBER_OF_CODES;
55 }
56
57 // Append or replace (default).
58 if ( isset( $args['method'] ) && 'append' === $args['method'] ) {
59 $codes_hashed = (array) get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );
60 }
61
62 for ( $i = 0; $i < $num_codes; $i++ ) {
63 $code = Authentication::get_code();
64 $codes_hashed[] = wp_hash_password( $code );
65 $codes[] = $code;
66 unset( $code );
67 }
68
69 update_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, $codes_hashed );
70
71 // Unhashed.
72 return $codes;
73 }
74
75 /**
76 * Generate codes and check remaining amount for user.
77 */
78 public static function run_ajax_generate_json() {
79 if ( isset( $_POST['user_id'] ) ) {
80 $user_id = (int) $_POST['user_id'];
81 $user = get_user_by( 'id', sanitize_text_field( $user_id ) );
82 } else {
83 $user = wp_get_current_user();
84 }
85
86 check_ajax_referer( 'wp-2fa-backup-codes-generate-json-' . $user->ID, 'nonce' );
87
88 // Setup the return data.
89 $codes = self::generate_codes( $user );
90 $count = self::codes_remaining_for_user( $user );
91 $i18n = array(
92 'count' => esc_html(
93 sprintf(
94 /* translators: %s: count */
95 _n( '%s unused code remaining.', '%s unused codes remaining.', $count, 'wp-2fa' ),
96 $count
97 )
98 ),
99 /* translators: %s: the site's domain */
100 'title' => esc_html__( 'Two-Factor Backup Codes for %s', 'wp-2fa' ),
101 );
102
103 // Send the response.
104 wp_send_json_success(
105 array(
106 'codes' => $codes,
107 'i18n' => $i18n,
108 )
109 );
110 }
111
112 /**
113 * Grab number of unused backup codes within the users possition.
114 *
115 * @param object $user User data.
116 * @return int Count of codes.
117 */
118 public static function codes_remaining_for_user( $user ) {
119 $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );
120 if ( is_array( $backup_codes ) && ! empty( $backup_codes ) ) {
121 return count( $backup_codes );
122 }
123 return 0;
124 }
125
126 /**
127 * Validate backup codes
128 *
129 * @param object $user User data.
130 * @param string $code The code we are checking.
131 * @return bool Is is valid or not.
132 */
133 public static function validate_code( $user, $code ) {
134 $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );
135 if ( is_array( $backup_codes ) && ! empty( $backup_codes ) ) {
136 foreach ( $backup_codes as $code_index => $code_hashed ) {
137 if ( wp_check_password( $code, $code_hashed, $user->ID ) ) {
138 self::delete_code( $user, $code_hashed );
139 return true;
140 }
141 }
142 }
143 return false;
144 }
145
146 /**
147 * Delete code once its used.
148 *
149 * @param object $user User data.
150 * @param string $code_hashed Code to delete.
151 */
152 public static function delete_code( $user, $code_hashed ) {
153 $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true );
154
155 // Delete the current code from the list since it's been used.
156 $backup_codes = array_flip( $backup_codes );
157 unset( $backup_codes[ $code_hashed ] );
158 $backup_codes = array_values( array_flip( $backup_codes ) );
159
160 // Update the backup code master list.
161 update_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, $backup_codes );
162 }
163
164 }
165