Skip to content

Commit

Permalink
Fix WP Telegram Login race condition to prevent duplicate Mini App us…
Browse files Browse the repository at this point in the history
…ers (#218)

* Fix WP Telegram Login race condition to prevent duplicate Mini App users

* Avoid double sanitization

* Ensure to retry every second

* Enhance actions and filters
  • Loading branch information
irshadahmad21 authored Dec 3, 2024
1 parent 437e984 commit 06fbeea
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-games-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wptelegram-login": patch
---

Fixed WP Telegram Login race condition to prevent duplicate Mini App users
12 changes: 10 additions & 2 deletions plugins/wptelegram-login/src/includes/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static function get_default_settings() {
*
* @since 1.10.3
*
* @param int $tg_user_id Telegram User ID.
* @param int|string $tg_user_id Telegram User ID.
*
* @return WP_User|false User object or false
*/
Expand All @@ -106,6 +106,14 @@ public static function get_user_by_telegram_id( $tg_user_id ) {

$users = get_users( $args );

return reset( $users );
$user = reset( $users );

/**
* Filter the user found by its Telegram ID.
*
* @param WP_User|false $user The user object or false.
* @param int|string $tg_user_id Telegram User ID.
*/
return apply_filters( 'wptelegram_login_get_user_by_telegram_id', $user, $tg_user_id );
}
}
69 changes: 41 additions & 28 deletions plugins/wptelegram-login/src/shared/LoginHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ public function telegram_login() {
try {
$auth_data = $this->validate_auth_data( $input );

// Add a lock using transients to prevent multiple concurrent requests.
$transient_key = 'wptelegram_login_' . $auth_data['id'];

$retry_count = 0;
$max_retries = 5;
while ( get_transient( $transient_key ) && $retry_count <= $max_retries ) {
++$retry_count;
sleep( 1 );
}

set_transient( $transient_key, current_time( 'mysql' ), $max_retries );

/**
* Fires before the user data is saved after validation.
*
Expand All @@ -70,6 +82,8 @@ public function telegram_login() {
*/
do_action( 'wptelegram_login_after_save_user_data', $wp_user_id, $auth_data );

delete_transient( $transient_key );

} catch ( Exception $e ) {
// phpcs:ignore WordPress.Security.EscapeOutput
wp_die( $e->getMessage(), esc_html__( 'Error:', 'wptelegram-login' ), [ 'back_link' => true ] );
Expand Down Expand Up @@ -261,6 +275,10 @@ public function validate_auth_data( $input_data ) {
$auth_data = ! empty( $auth_data['user'] ) ? Utils::sanitize( json_decode( $auth_data['user'], true ) ) : [];
}

if ( empty( $auth_data['id'] ) || empty( $auth_data['first_name'] ) ) {
throw new Exception( esc_html__( 'Invalid! The data is incomplete', 'wptelegram-login' ) );
}

/**
* Filter the validated auth data.
*
Expand Down Expand Up @@ -435,22 +453,16 @@ public function unique_email( $user, $host ) {
*
* @since 1.0.0
*
* @param array $data The user data received from Telegram.
* @param array $auth_data The user data received from Telegram.
* @throws Exception The exception.
*/
public function save_telegram_user_data( $data ) {

if ( empty( $data['id'] ) || empty( $data['first_name'] ) ) {
throw new Exception( esc_html__( 'Invalid! The data is incomplete', 'wptelegram-login' ) );
}

$data = array_map( 'htmlspecialchars', $data );
public function save_telegram_user_data( $auth_data ) {

// Check if the request is from a logged in user.
$cur_user = wp_get_current_user();

// Check if the user is signing in again.
$ret_user = Utils::get_user_by_telegram_id( $data['id'] );
$ret_user = Utils::get_user_by_telegram_id( $auth_data['id'] );

$existing_user_id = null;

Expand Down Expand Up @@ -478,9 +490,9 @@ public function save_telegram_user_data( $data ) {
* It means that the user must first create an account and connect it to Telegram to be able to use Telegram Login.
*
* @param bool $disable_signup Whether to disable sign up via Telegram.
* @param array $data The user details.
* @param array $auth_data The user data from Telegram.
*/
$disable_signup = (bool) apply_filters( 'wptelegram_login_disable_signup', $disable_signup, $data );
$disable_signup = (bool) apply_filters( 'wptelegram_login_disable_signup', $disable_signup, $auth_data );

if ( $disable_signup ) {

Expand All @@ -498,46 +510,46 @@ public function save_telegram_user_data( $data ) {
* - [Examples](./examples/always_update_user_data.md)
*
* @param bool $always_update Whether to always update the user data.
* @param array $data The user details.
* @param array $auth_data The user data from Telegram.
* @param int|NULL $existing_user_id Existing WP User ID.
*/
$always_update_user_data = apply_filters( 'wptelegram_login_always_update_user_data', $always_update, $data, $existing_user_id );
$always_update_user_data = apply_filters( 'wptelegram_login_always_update_user_data', $always_update, $auth_data, $existing_user_id );

if ( $existing_user_id && ! $always_update_user_data ) {
return $existing_user_id;
}

return $this->save_user_data( $data, $existing_user_id );
return $this->save_user_data( $auth_data, $existing_user_id );
}

/**
* Save or update the user data.
*
* @since 1.0.0
*
* @param array $data The user details.
* @param array $auth_data The user data from Telegram.
* @param int|NULL $wp_user_id Existing WP User ID.
*
* @throws Exception The exception.
*
* @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not be created
*/
public function save_user_data( $data, $wp_user_id = null ) {
public function save_user_data( $auth_data, $wp_user_id = null ) {

/**
* Filter the user data before saving the user in the database.
*
* @param array $data The user details.
* @param array $auth_data The user data from Telegram.
* @param int|NULL $wp_user_id Existing WP User ID.
*/
$data = apply_filters( 'wptelegram_login_save_user_data', $data, $wp_user_id );
$auth_data = apply_filters( 'wptelegram_login_save_user_data', $auth_data, $wp_user_id );

// The data fields received.
$id = $data['id'];
$first_name = $data['first_name'];
$last_name = isset( $data['last_name'] ) ? $data['last_name'] : '';
$tg_username = isset( $data['username'] ) ? $data['username'] : '';
$photo_url = isset( $data['photo_url'] ) ? $data['photo_url'] : '';
$id = $auth_data['id'];
$first_name = $auth_data['first_name'];
$last_name = isset( $auth_data['last_name'] ) ? $auth_data['last_name'] : '';
$tg_username = isset( $auth_data['username'] ) ? $auth_data['username'] : '';
$photo_url = isset( $auth_data['photo_url'] ) ? $auth_data['photo_url'] : '';
$username = $tg_username;

if ( is_null( $wp_user_id ) ) { // New user.
Expand Down Expand Up @@ -566,9 +578,10 @@ public function save_user_data( $data, $wp_user_id = null ) {
/**
* Filter the user data before inserting the user into the database.
*
* @param array $userdata The user data.
* @param array $userdata The user data to insert into the database.
* @param array $auth_data The user data from Telegram.
*/
$userdata = apply_filters( 'wptelegram_login_insert_user_data', $userdata );
$userdata = apply_filters( 'wptelegram_login_insert_user_data', $userdata, $auth_data );

$wp_user_id = wp_insert_user( $userdata );

Expand All @@ -580,10 +593,10 @@ public function save_user_data( $data, $wp_user_id = null ) {
* Fires after the user is successfully inserted into the database.
*
* @param int $wp_user_id The WordPress user ID.
* @param array $userdata The user data.
* @param array $userdata The user data inserted into the database.
* @param array $auth_data The user data from Telegram.
*/
do_action( 'wptelegram_login_after_insert_user', $wp_user_id, $userdata );

do_action( 'wptelegram_login_after_insert_user', $wp_user_id, $userdata, $auth_data );
}

/* Update the user */
Expand Down

0 comments on commit 06fbeea

Please sign in to comment.