From e6c3ea1c1db9e6d13b9b7a73a1ed918a526da2a7 Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 2 Dec 2024 15:28:59 +0200 Subject: [PATCH 01/15] Allow filtering the email token length --- providers/class-two-factor-email.php | 4 +++- readme.txt | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/providers/class-two-factor-email.php b/providers/class-two-factor-email.php index da7a7c60..a3d86b43 100644 --- a/providers/class-two-factor-email.php +++ b/providers/class-two-factor-email.php @@ -72,7 +72,9 @@ public function get_alternative_provider_label() { * @return string */ public function generate_token( $user_id ) { - $token = $this->get_code(); + $token_length = (int) apply_filters( 'two_factor_token_length', 8 ); + + $token = $this->get_code( $token_length ); update_user_meta( $user_id, self::TOKEN_META_KEY_TIMESTAMP, time() ); update_user_meta( $user_id, self::TOKEN_META_KEY, wp_hash( $token ) ); diff --git a/readme.txt b/readme.txt index 8c2ff974..07cd5777 100644 --- a/readme.txt +++ b/readme.txt @@ -28,6 +28,7 @@ Here is a list of action and filter hooks provided by the plugin: - `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. - `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. - `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. +- `two_factor_token_length` filter overrides the default 8 character count for email tokens. == Frequently Asked Questions == From 1b103109da01b2f315f9b2355c53477461f15edd Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 2 Dec 2024 15:29:12 +0200 Subject: [PATCH 02/15] Allow filtering the backup code length --- providers/class-two-factor-backup-codes.php | 10 +++++++++- readme.txt | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/providers/class-two-factor-backup-codes.php b/providers/class-two-factor-backup-codes.php index 12601d5c..89389238 100644 --- a/providers/class-two-factor-backup-codes.php +++ b/providers/class-two-factor-backup-codes.php @@ -239,8 +239,16 @@ public function generate_codes( $user, $args = '' ) { $codes_hashed = (array) get_user_meta( $user->ID, self::BACKUP_CODES_META_KEY, true ); } + /** + * Customize the character count of the backup codes. + * + * @var int $code_length Length of the backup code. + * @var WP_User $user User object. + */ + $code_length = (int) apply_filters( 'two_factor_backup_code_length', 8, $user ); + for ( $i = 0; $i < $num_codes; $i++ ) { - $code = $this->get_code(); + $code = $this->get_code( $code_length ); $codes_hashed[] = wp_hash_password( $code ); $codes[] = $code; unset( $code ); diff --git a/readme.txt b/readme.txt index 07cd5777..d0853539 100644 --- a/readme.txt +++ b/readme.txt @@ -29,6 +29,7 @@ Here is a list of action and filter hooks provided by the plugin: - `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. - `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. - `two_factor_token_length` filter overrides the default 8 character count for email tokens. +- `two_factor_backup_code_length` filter overrides the default 8 character count for backup codes. Providers the `WP_User` of the associated user as the second argument. == Frequently Asked Questions == From bbcd0415f9b517d753520ed918d982b068332f72 Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 2 Dec 2024 15:40:57 +0200 Subject: [PATCH 03/15] Add tests for the backup code length filter --- .../class-two-factor-backup-codes.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/providers/class-two-factor-backup-codes.php b/tests/providers/class-two-factor-backup-codes.php index a926044e..fb134b60 100644 --- a/tests/providers/class-two-factor-backup-codes.php +++ b/tests/providers/class-two-factor-backup-codes.php @@ -194,4 +194,25 @@ public function test_delete_code() { $this->provider->delete_code( $user, $backup_codes[0] ); $this->assertEquals( 1, $this->provider->codes_remaining_for_user( $user ) ); } + + public function test_backup_code_length_filter() { + $user = new WP_User( self::factory()->user->create() ); + + $code_default = $this->provider->generate_codes( $user, array( 'number' => 1 ) ); + + add_filter( + 'two_factor_backup_code_length', + function() { + return 7; + } + ); + + $code_custom_length = $this->provider->generate_codes( $user, array( 'number' => 1 ) ); + + $this->assertNotEquals( strlen( $code_custom_length[0] ), strlen( $code_default[0] ), 'Backup code length can be adjusted via filter' ); + + $this->assertEquals( 7, strlen( $code_custom_length[0] ), 'Backup code length matches the filtered length' ); + + remove_all_filters( 'two_factor_backup_code_length' ); + } } From 61a74896b7ec5ebb1a207df104c01e78ee19d801 Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 2 Dec 2024 15:45:16 +0200 Subject: [PATCH 04/15] Add tests for email token length too --- tests/providers/class-two-factor-email.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/providers/class-two-factor-email.php b/tests/providers/class-two-factor-email.php index 93e5dc76..0b5a154c 100644 --- a/tests/providers/class-two-factor-email.php +++ b/tests/providers/class-two-factor-email.php @@ -352,4 +352,24 @@ public function test_tokens_can_expire() { ); } + public function test_custom_token_length() { + $user_id = self::factory()->user->create(); + + $default_token = $this->provider->generate_token( $user_id ); + + add_filter( + 'two_factor_token_length', + function() { + return 15; + } + ); + + $custom_token = $this->provider->generate_token( $user_id ); + + $this->assertNotEquals( strlen( $default_token ), strlen( $custom_token ), 'Token length is different due to filter' ); + $this->assertEquals( 15, strlen( $custom_token ), 'Token length matches the filter value' ); + + remove_all_filters( 'two_factor_token_length' ); + } + } From 01cfb60afbaf4ef6829f7869fee9e9edce6a5ced Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 11:48:31 +0200 Subject: [PATCH 05/15] Avoid confusion about the token type --- providers/class-two-factor-email.php | 2 +- readme.txt | 2 +- tests/providers/class-two-factor-email.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/providers/class-two-factor-email.php b/providers/class-two-factor-email.php index a3d86b43..3122ea1b 100644 --- a/providers/class-two-factor-email.php +++ b/providers/class-two-factor-email.php @@ -72,7 +72,7 @@ public function get_alternative_provider_label() { * @return string */ public function generate_token( $user_id ) { - $token_length = (int) apply_filters( 'two_factor_token_length', 8 ); + $token_length = (int) apply_filters( 'two_factor_email_token_length', 8 ); $token = $this->get_code( $token_length ); diff --git a/readme.txt b/readme.txt index d0853539..d72c194d 100644 --- a/readme.txt +++ b/readme.txt @@ -28,7 +28,7 @@ Here is a list of action and filter hooks provided by the plugin: - `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. - `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. - `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. -- `two_factor_token_length` filter overrides the default 8 character count for email tokens. +- `two_factor_email_token_length` filter overrides the default 8 character count for email tokens. - `two_factor_backup_code_length` filter overrides the default 8 character count for backup codes. Providers the `WP_User` of the associated user as the second argument. == Frequently Asked Questions == diff --git a/tests/providers/class-two-factor-email.php b/tests/providers/class-two-factor-email.php index 0b5a154c..6b10bcf6 100644 --- a/tests/providers/class-two-factor-email.php +++ b/tests/providers/class-two-factor-email.php @@ -358,7 +358,7 @@ public function test_custom_token_length() { $default_token = $this->provider->generate_token( $user_id ); add_filter( - 'two_factor_token_length', + 'two_factor_email_token_length', function() { return 15; } @@ -369,7 +369,7 @@ function() { $this->assertNotEquals( strlen( $default_token ), strlen( $custom_token ), 'Token length is different due to filter' ); $this->assertEquals( 15, strlen( $custom_token ), 'Token length matches the filter value' ); - remove_all_filters( 'two_factor_token_length' ); + remove_all_filters( 'two_factor_email_token_length' ); } } From 33f0060948e82d87787e996fa07925d6b02982dc Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 11:54:05 +0200 Subject: [PATCH 06/15] Deprecate the old filter instead for clarity --- providers/class-two-factor-email.php | 13 ++++++++++++- readme.txt | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/providers/class-two-factor-email.php b/providers/class-two-factor-email.php index 3122ea1b..4bf8eff6 100644 --- a/providers/class-two-factor-email.php +++ b/providers/class-two-factor-email.php @@ -144,6 +144,17 @@ public function user_token_lifetime( $user_id ) { public function user_token_ttl( $user_id ) { $token_ttl = 15 * MINUTE_IN_SECONDS; + /** + * Number of seconds the token is considered valid + * after the generation. + * + * @deprecated 0.11.0 Use {@see 'two_factor_email_token_ttl'} instead. + * + * @param integer $token_ttl Token time-to-live in seconds. + * @param integer $user_id User ID. + */ + $token_ttl = (int) apply_filters_deprecated( 'two_factor_token_ttl', array( $token_ttl, $user_id ), '0.7.0', 'two_factor_email_token_ttl' ); + /** * Number of seconds the token is considered valid * after the generation. @@ -151,7 +162,7 @@ public function user_token_ttl( $user_id ) { * @param integer $token_ttl Token time-to-live in seconds. * @param integer $user_id User ID. */ - return (int) apply_filters( 'two_factor_token_ttl', $token_ttl, $user_id ); + return (int) apply_filters( 'two_factor_email_token_ttl', $token_ttl, $user_id ); } /** diff --git a/readme.txt b/readme.txt index d72c194d..4fd95f27 100644 --- a/readme.txt +++ b/readme.txt @@ -27,7 +27,7 @@ Here is a list of action and filter hooks provided by the plugin: - `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. - `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. - `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. -- `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. +- `two_factor_email_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. - `two_factor_email_token_length` filter overrides the default 8 character count for email tokens. - `two_factor_backup_code_length` filter overrides the default 8 character count for backup codes. Providers the `WP_User` of the associated user as the second argument. From 12571b1babacb9e94618e9ff4080612afdec769e Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 12:15:42 +0200 Subject: [PATCH 07/15] Add tests for the email token TTL filter --- tests/providers/class-two-factor-email.php | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/providers/class-two-factor-email.php b/tests/providers/class-two-factor-email.php index 6b10bcf6..bfcfb681 100644 --- a/tests/providers/class-two-factor-email.php +++ b/tests/providers/class-two-factor-email.php @@ -372,4 +372,47 @@ function() { remove_all_filters( 'two_factor_email_token_length' ); } + /** + * Test the email token TTL. + * + * @expectedDeprecated two_factor_token_ttl + */ + public function test_email_token_ttl() { + $this->assertEquals( + 15 * MINUTE_IN_SECONDS, + $this->provider->user_token_ttl( 123 ), + 'The email token matches the default TTL' + ); + + add_filter( + 'two_factor_email_token_ttl', + function() { + return 42; + } + ); + + $this->assertEquals( + 42, + $this->provider->user_token_ttl( 123 ), + 'The email token ttl can be filtered' + ); + + remove_all_filters( 'two_factor_email_token_ttl' ); + + add_filter( + 'two_factor_token_ttl', + function() { + return 66; + } + ); + + $this->assertEquals( + 66, + $this->provider->user_token_ttl( 123 ), + 'The email token matches can be filtered with the deprecated filter' + ); + + remove_all_filters( 'two_factor_token_ttl' ); + } + } From df8cff258d9f9fd6e89803c3185d6aaad4e6438a Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 12:15:49 +0200 Subject: [PATCH 08/15] Fix the version reference --- providers/class-two-factor-email.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/class-two-factor-email.php b/providers/class-two-factor-email.php index 4bf8eff6..d7fc390c 100644 --- a/providers/class-two-factor-email.php +++ b/providers/class-two-factor-email.php @@ -153,7 +153,7 @@ public function user_token_ttl( $user_id ) { * @param integer $token_ttl Token time-to-live in seconds. * @param integer $user_id User ID. */ - $token_ttl = (int) apply_filters_deprecated( 'two_factor_token_ttl', array( $token_ttl, $user_id ), '0.7.0', 'two_factor_email_token_ttl' ); + $token_ttl = (int) apply_filters_deprecated( 'two_factor_token_ttl', array( $token_ttl, $user_id ), '0.11.0', 'two_factor_email_token_ttl' ); /** * Number of seconds the token is considered valid From fc95741d6a91ad67ff5925107d20acb815b6a5bf Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 12:28:34 +0200 Subject: [PATCH 09/15] Match the input count and placeholder --- providers/class-two-factor-email.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/providers/class-two-factor-email.php b/providers/class-two-factor-email.php index d7fc390c..9b1b124f 100644 --- a/providers/class-two-factor-email.php +++ b/providers/class-two-factor-email.php @@ -63,6 +63,17 @@ public function get_alternative_provider_label() { return __( 'Send a code to your email', 'two-factor' ); } + private function get_token_length() { + /** + * Number of characters in the email token. + * + * @param int $token_length Number of characters in the email token. + */ + $token_length = (int) apply_filters( 'two_factor_email_token_length', 8 ); + + return $token_length; + } + /** * Generate the user token. * @@ -72,9 +83,7 @@ public function get_alternative_provider_label() { * @return string */ public function generate_token( $user_id ) { - $token_length = (int) apply_filters( 'two_factor_email_token_length', 8 ); - - $token = $this->get_code( $token_length ); + $token = $this->get_code( $this->get_token_length() ); update_user_meta( $user_id, self::TOKEN_META_KEY_TIMESTAMP, time() ); update_user_meta( $user_id, self::TOKEN_META_KEY, wp_hash( $token ) ); @@ -272,12 +281,15 @@ public function authentication_page( $user ) { $this->generate_and_email_token( $user ); } + $token_length = $this->get_token_length(); + $token_placeholder = str_repeat( 'X', $token_length ); + require_once ABSPATH . '/wp-admin/includes/template.php'; ?>

- +

From f7ac59bdefa48d9310e439e0c59647cc9d634596 Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 12:42:53 +0200 Subject: [PATCH 10/15] Missing comment --- providers/class-two-factor-email.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/providers/class-two-factor-email.php b/providers/class-two-factor-email.php index 3f46d7a2..038e35cb 100644 --- a/providers/class-two-factor-email.php +++ b/providers/class-two-factor-email.php @@ -63,6 +63,11 @@ public function get_alternative_provider_label() { return __( 'Send a code to your email', 'two-factor' ); } + /** + * Get the email token length. + * + * @return int Email token string length. + */ private function get_token_length() { /** * Number of characters in the email token. From ea5dbcb03357a0cf00165ce3ee4c486adde39a07 Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 12:43:07 +0200 Subject: [PATCH 11/15] A placeholder for backup codes as well --- providers/class-two-factor-backup-codes.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/providers/class-two-factor-backup-codes.php b/providers/class-two-factor-backup-codes.php index fd2e04ee..0825da41 100644 --- a/providers/class-two-factor-backup-codes.php +++ b/providers/class-two-factor-backup-codes.php @@ -334,11 +334,14 @@ public static function codes_remaining_for_user( $user ) { */ public function authentication_page( $user ) { require_once ABSPATH . '/wp-admin/includes/template.php'; + + $code_placeholder = str_repeat( '0', self::NUMBER_OF_CODES ); + ?>


- +

Date: Thu, 9 Jan 2025 12:47:48 +0200 Subject: [PATCH 12/15] Use the backup code length instead of the code count --- providers/class-two-factor-backup-codes.php | 22 +++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/providers/class-two-factor-backup-codes.php b/providers/class-two-factor-backup-codes.php index 0825da41..850300a6 100644 --- a/providers/class-two-factor-backup-codes.php +++ b/providers/class-two-factor-backup-codes.php @@ -214,6 +214,18 @@ public function user_options( $user ) { ID, self::BACKUP_CODES_META_KEY, true ); } - /** - * Customize the character count of the backup codes. - * - * @var int $code_length Length of the backup code. - * @var WP_User $user User object. - */ - $code_length = (int) apply_filters( 'two_factor_backup_code_length', 8, $user ); + $code_length = $this->get_backup_code_length( $user ); for ( $i = 0; $i < $num_codes; $i++ ) { $code = $this->get_code( $code_length ); @@ -335,7 +341,7 @@ public static function codes_remaining_for_user( $user ) { public function authentication_page( $user ) { require_once ABSPATH . '/wp-admin/includes/template.php'; - $code_placeholder = str_repeat( '0', self::NUMBER_OF_CODES ); + $code_placeholder = str_repeat( 'X', $this->get_backup_code_length( $user ) ); ?>


From 8913ee1683d63ff6f60dd8fb35c1eb93510ec44b Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 12:57:22 +0200 Subject: [PATCH 13/15] The paragraphs already have enough space --- providers/class-two-factor-backup-codes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/providers/class-two-factor-backup-codes.php b/providers/class-two-factor-backup-codes.php index 850300a6..322cd2ed 100644 --- a/providers/class-two-factor-backup-codes.php +++ b/providers/class-two-factor-backup-codes.php @@ -344,7 +344,7 @@ public function authentication_page( $user ) { $code_placeholder = str_repeat( 'X', $this->get_backup_code_length( $user ) ); ?> -


+

From 0eac5d87dbdd3e0a7419a22cdabbc136f5e5487f Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Thu, 9 Jan 2025 12:59:10 +0200 Subject: [PATCH 14/15] Add a comment --- providers/class-two-factor-backup-codes.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/providers/class-two-factor-backup-codes.php b/providers/class-two-factor-backup-codes.php index 322cd2ed..adab5cda 100644 --- a/providers/class-two-factor-backup-codes.php +++ b/providers/class-two-factor-backup-codes.php @@ -214,6 +214,13 @@ public function user_options( $user ) { Date: Thu, 9 Jan 2025 13:06:27 +0200 Subject: [PATCH 15/15] Fix the character count --- providers/class-two-factor-backup-codes.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/providers/class-two-factor-backup-codes.php b/providers/class-two-factor-backup-codes.php index adab5cda..2a3345b4 100644 --- a/providers/class-two-factor-backup-codes.php +++ b/providers/class-two-factor-backup-codes.php @@ -348,13 +348,14 @@ public static function codes_remaining_for_user( $user ) { public function authentication_page( $user ) { require_once ABSPATH . '/wp-admin/includes/template.php'; - $code_placeholder = str_repeat( 'X', $this->get_backup_code_length( $user ) ); + $code_length = $this->get_backup_code_length( $user ); + $code_placeholder = str_repeat( 'X', $code_length ); ?>

- +