ID ) ) ) { return; } // Return if we are not out of codes. if ( $this->is_available_for_user( $user ) ) { return; } ?>

regenerate!', 'it-l10n-ithemes-security-pro' ), esc_url( get_edit_user_link( $user->ID ) . '#two-factor-backup-codes' ) ); ?>

ID ); $count = self::codes_remaining_for_user( $user ); ?>

ID, self::BACKUP_CODES_META_KEY, true ); } for ( $i = 0; $i < $num_codes; $i++ ) { $code = $this->get_code(); $codes_hashed[] = wp_hash_password( $code ); $codes[] = $code; unset( $code ); } update_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, $codes_hashed ); // Unhashed. return $codes; } /** * Generates a JSON object of backup codes. * * @since 0.1-dev */ public function ajax_generate_json() { $user = get_user_by( 'id', sanitize_text_field( $_POST['user_id'] ) ); check_ajax_referer( 'two-factor-backup-codes-generate-json-' . $user->ID, 'nonce' ); // Setup the return data. $codes = $this->generate_codes( $user ); $count = self::codes_remaining_for_user( $user ); $i18n = esc_html( sprintf( _n( '%s unused code remaining.', '%s unused codes remaining.', $count ), $count ) ); // Send the response. wp_send_json_success( array( 'codes' => $codes, 'i18n' => $i18n ) ); } /** * Returns the number of unused codes for the specified user * * @param WP_User $user WP_User object of the logged-in user. * @return int $int The number of unused codes remaining */ public static function codes_remaining_for_user( $user ) { $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true ); if ( is_array( $backup_codes ) && ! empty( $backup_codes ) ) { return count( $backup_codes ); } return 0; } /** * Prints the form that prompts the user to authenticate. * * @since 0.1-dev * * @param WP_User $user WP_User object of the logged-in user. */ public function authentication_page( $user ) { require_once( ABSPATH . '/wp-admin/includes/template.php' ); ?>


validate_code( $user, trim( $_POST['two-factor-backup-code'] ) ); } /** * Validates a backup code. * * Backup Codes are single use and are deleted upon a successful validation. * * @since 0.1-dev * * @param WP_User $user WP_User object of the logged-in user. * @param int $code The backup code. * @return boolean */ public function validate_code( $user, $code ) { $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true ); foreach ( $backup_codes as $code_index => $code_hashed ) { if ( wp_check_password( $code, $code_hashed, $user->ID ) ) { $this->delete_code( $user, $code_hashed ); return true; } } return false; } /** * Deletes a backup code. * * @since 0.1-dev * * @param WP_User $user WP_User object of the logged-in user. * @param string $code_hashed The hashed the backup code. */ public function delete_code( $user, $code_hashed ) { $backup_codes = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true ); // Delete the current code from the list since it's been used. $backup_codes = array_flip( $backup_codes ); unset( $backup_codes[ $code_hashed ] ); $backup_codes = array_values( array_flip( $backup_codes ) ); // Update the backup code master list. update_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, $backup_codes ); } public function description() { echo '

' . __( 'Provide a set of one-time use codes that can be used to login in the event the primary two-factor method is lost. Note: these codes are intended to be stored in a secure location.', 'it-l10n-ithemes-security-pro' ) . '

'; } /** * @inheritDoc */ public function get_on_board_dashicon() { return 'backup'; } /** * @inheritDoc */ public function get_on_board_label() { return esc_html__( 'Backup Codes', 'it-l10n-ithemes-security-pro' ); } /** * @inheritDoc */ public function get_on_board_description() { return esc_html__( 'A list of one-time codes you can use if you lose access to your device.', 'it-l10n-ithemes-security-pro' ); } /** * @inheritDoc */ public function has_on_board_configuration() { return true; } /** * @inheritDoc */ public function get_on_board_config( WP_User $user ) { $is_configured = get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true ) !== ''; if ( $is_configured && ! get_user_meta( $user->ID, self::TEMP_FLAG_META_KEY, true ) ) { $config = array( 'code_count' => self::codes_remaining_for_user( $user ), 'codes' => array(), 'is_configured' => true, ); } else { update_user_meta( $user->ID, self::TEMP_FLAG_META_KEY, true ); $config = array( 'codes' => $codes = $this->generate_codes( $user ), 'code_count' => count( $codes ), 'is_configured' => false, ); } return $config; } /** * @inheritDoc */ public function handle_ajax_on_board( WP_User $user, array $data ) { if ( $data['itsec_method'] === 'generate-backup-codes' ) { $new = $this->generate_codes( $user ); wp_send_json_success( array( 'message' => esc_html__( 'Codes Generated', 'it-l10n-ithemes-security-pro' ), 'codes' => $new, 'code_count' => count( $new ), ) ); } } public function configure_via_cli( WP_User $user, array $args ) { $codes = $this->generate_codes( $user ); $as_string = implode( ' ', $codes ); if ( empty( $args['porcelain'] ) ) { WP_CLI::log( sprintf( 'Backup Codes: %s', $as_string ) ); } else { WP_CLI::log( $as_string ); } } }