FlowMattic: Sync Users Between Sites

Tech Articles | January 3, 2025 | Automation, Coding, FlowMattic, Wordpress

A typical user request to FlowMattic Support and request of mine has been using FlowMattic to sync site users, i.e. a user is created on Site A and is automatically synced to Site B.

The most typical use case is course /membership sites. We have a main website where we sell or do other things, but we want the same user to be able to log in with the same role on our membership site.

The video and write-up only cover syncing a username, first name, last name, email address, role and password between 2 or more sites. Any additional metadata can be sent but would be unique to each use case, and you’d need to work out what to send and change for your specific use case.

We are using two workflows on each site, one to receive the data for the user created via webhook and one to send the data on being triggered by creating a new user.

We are using a single snippet with 3no functions in it they are extractRoles (this extracts the user roles from the sent data), retrieve_hashed_password (this extracts the password hash from the sending site) and check_and_update_password (this updates the password with the hash on the receiving site) to make sure the same password works correctly.

Send Work Flow:

The Send workflow is simple and is triggered by creating a new user. You can see a copy of the workflow below, so we are triggered. We then extract the password hash using retrieve_hashed_password() and then use the API module to send this to the receiving site’s webhook.

Send Workflow

The data we send in the Post API are listed below as individual parameters and comprise the data from the new user trigger and the retrieve_hased_password() function.

Receiving Work Flow:

The receiving workflow is a little more complex, but this has more to do with the order in which we do the items rather than what we are doing, as some of it seems counterintuitive. Once you understand the reasons for the order it all becomes clear.

We receive our details from our sending site via API post request from the sending site, and this means the following data is available to us

Now, the step is to use another PHP function to extract the role; when transferring between the sites, the role seems to become joined with the variable name (1) roles_editor, so what we need to do is get the roles out i.e. editor and the easiest way to do this given the combining is to use the fm_webhook_data (2) and put it through extractRoles() to get out the roles in our case “editor”

We then create our user; now, in the password box for this, I use the password hash, but it won’t work; the reason for this is that when submitted, the hash will be rehashed and will subsequently not work. The password will become the hash.

We fix this with a snippet. We have already hashed the password on-site, so what we need to do is just place it in the database against the correct user; we do this with another snippet, check_and_update_password(), which will push the hash directly to the database without it being rehashed again.

With that, our user is now synced between the two sites, and our user can log in to both sites with the username and password as set.

Recommended Upgrade: If you are using this in production, I strongly recommend you use some form of authentication between your API send and your webhook and add a check for this verification; while check_and_update_password() has some protection from SQL injection, there is always a risk when sending data between sites.

The Snippet

The following code is used within this tutorial and, as with all code, is provided as-is but works within the example tutorial.

<?php
/* This Snippet extracts the roles out of the json transmitted i.e fm_webhook_data */
function extractRoles($jsonData) {
    // Decode the JSON string into an associative array
    $data = json_decode($jsonData, true);
    
    // Check if 'roles' key exists and is an array
    if (isset($data['roles']) && is_array($data['roles'])) {
        // Extract the keys of the roles array where the value is true
        $roles = array_keys(array_filter($data['roles']));
        
        // Return all roles as an array
        return $roles;
    }
    
    // If no roles found, return an empty array
    return [];
}
/* This snippet pulls a users hashed password from the database for sending */
function retrieve_hashed_password($user_id) {
    global $wpdb;

    // Retrieve the hashed password from the `wp_users` table.
    $hashed_password = $wpdb->get_var($wpdb->prepare(
        "SELECT user_pass FROM {$wpdb->users} WHERE ID = %d",
        $user_id
    ));

    if ($hashed_password) {
        // Return the hashed password and user ID as an array.
        return [
            'user_id' => $user_id,
            'hashed_password' => $hashed_password,
        ];
    }

    return false; // Return false if the user ID doesn't exist.
}

/* This Function updates the user password hash correctly so the same password works on both sites */
function check_and_update_password($user_id, $hashed_password) {
    global $wpdb;

    // Sanitize inputs to ensure they are valid and secure.
    $user_id = intval($user_id); // Ensure the user ID is an integer.
    $hashed_password = sanitize_text_field($hashed_password); // Ensure the hashed password is clean.

    // Check if the user exists in the database.
    $user_exists = $wpdb->get_var($wpdb->prepare(
        "SELECT ID FROM {$wpdb->users} WHERE ID = %d",
        $user_id
    ));

    if ($user_exists) {
        // Update the `user_pass` field with the received hashed password.
        $updated = $wpdb->update(
            $wpdb->users,
            ['user_pass' => $hashed_password],
            ['ID' => $user_id],
            ['%s'],
            ['%d']
        );

        if ($updated !== false) {
            return 'Password updated successfully!';
        }

        return 'Failed to update the password.';
    } else {
        return 'User does not exist. Consider creating the user first.';
    }
}
PHP

Accompanying YouTube Video

The following video shows you a little more detail about the setup and usage of this process

Support the Author

buy me a coffee
Really Useful Plugin Logo
Appoligies for any spelling and grammer issue. As a dyslexic i need to rely on tools for this they like me are not perfect but I do try my best