0
votes

I'm creating a module in Drupal to keep the users synchronised between the Drupal site (old) and a Wordpress site (new). The sites were created separately, the WP site does not has its own domain, it is installed in a subfolder and the users can only access it using a link in the Drupal site. So the task is always in the Drupal->Wordpress direcction, create a user in Drupal -> create it in Wordpress, login a user to Drupal -> Log in the user in Wordpress.

I created a Drupal hook mymodule_user_insert() which basically takes the registered $account variable and makes a POST with it to the WP site, the WP plugin that receives the object creates a WP user with the same email and password and then calls the WP function to generate the WP_logged_in_cookie (basically authenticates the user), this cookie is returned in the POST response headers.

I use the the Drupal function drupal_http_request() to make the post and capture the response and headers, as only generating the cookie in the WP site did not make the user auto login in WP I used a variation of the code in http://forumone.com/insights/proxying-cookies-drupal/ which gets the cookies from the POST response headers and sets them in the Drupal module.

// sends a newly created user information to the wordpress site to keep it up to date
function mymodule_user_insert(&$edit, $account, $category) {
    // export users server url
    global $base_url;
    $url = $base_url.'/wpsubfolder/wp-admin/admin-ajax.php';

    // collect data and set action
    $data = array();
    $data['action'] = 'wp_plugin_create_user';
    $data['username'] = $account->name;
    $data['email'] = $account->mail;
    $data['password'] = $account->pass;
    $data['referer'] = $base_url;

    // send POST request
    $response = drupal_http_request($url, array(
           'method' => 'POST', 
           'data' => http_build_query($data), 
           'max_redirects' => 0,
           'headers' => array('Content-Type' => 'application/x-www-form-urlencoded')
        )
    );

    // get cookies from headers and set them here
    if (200 == $response->code) {
        foreach ($response->headers as $key => $value) {
            if (strcasecmp($key, 'set-cookie') == 0) {
                drupal_add_http_header($key, $value);
            }
        }
    // report any errors
    } elseif(isset($response->error)) {
        drupal_set_message(t($response->data), 'error');
    }

    return $response->data;
}

This works just fine, every time you create an account in the Drupal site it creates it in both sites and if you got to WP or Drupal you are already logged in both.

I moved then to create the Drupal hook mymodule_user_login() with almost the same code, with the difference that POST to a different function in the WP plugin which does not have the create_user code it just checks if the email exists and then authenticates the user creating the same cookie as before.

// Captures login information and sends post request to wp to login user
function mymodule_user_login(&$edit, $account) {
    // was login successfull
    if (user_is_logged_in()) {
        // export users server url
        global $base_url;
        $url = $base_url.'/wpsubfolder/wp-admin/admin-ajax.php';

        // collect data and set action
        $data = array();
        $data['action'] = 'wp_plugin_login_user';
        $data['username'] = $account->name;
        $data['email'] = $account->mail;
        $data['password'] = $account->pass;
        $data['referer'] = $base_url;

        // send POST request
        $response = drupal_http_request($url, array(
               'method' => 'POST', 
               'data' => http_build_query($data), 
               'max_redirects' => 0,
               'headers' => array('Content-Type' => 'application/x-www-form-urlencoded')
            )
        );

        // get cookies from headers
        if (200 == $response->code) {
            foreach ($response->headers as $key => $value) {

                if ($key == 'set-cookie') {
                    $cookies = preg_split('/(?<=\S),(?:\S)/', $value);
                                    // ADD WORDPRESS COOKIE BUT NOT DRUPAL TO AVOID OVERRIDING IT
                    foreach ($cookies as $cookie) {
                        // is wordpres cookie
                        if(strpos($cookie, 'wordpress') !== false) {
                            drupal_add_http_header($key, $value);
                        // other cookies, ignore drupal session cookie as it's already set
                        } elseif(strpos($cookie, 'SESS') === 0) {
                            // Send separately since drupal_add_http_header concatenates with commas
                            header($key . ': ' . $cookie, FALSE);
                        }
                    }
                }

            }

        } elseif(isset($response->error)) {
            // report any errors
            drupal_set_message(t($response->data), 'error');
        }
    }
}

The problem comes when receiving the headers back in the drupal login hook function, for some reason I end up logged in to WP but not in Drupal, but if I don't set the WP cookie in the Drupal headers when I receive them, the user is logged in and redirected normally to his account, but again the WP user is not automatically logged in.

As you can see in the code I thought that maybe I was overwriting the Drupal session cookie so I added a condition to ignore any cookie that started with the SESS prefix, but even like that it still does not work.

Is there a step I'm missing or is there an easier way to log a user that exists in both databases in both sites at the same time?

1
i don't know. It seems like a weird way to create and login user via POST requests, when you have access to both databases. I used to do something similar but it was from Cakephp to wordpress. I used API functions provided by WP to create and login user. codex.wordpress.org/Function_Reference/wp_create_user codex.wordpress.org/Function_Reference/wp_signon - Funky Dude
I did not include the WP code so the question was not that long, but actually I use both this functions to create and login the user, but in the end I also use codex.wordpress.org/Function_Reference/wp_set_auth_cookie the WP code works, if I do a POST from any test tool like POSTman or a test PHP the user is logged in, but if I do the POST from Drupal the cookie is not automatically added, although it is returned in the headers. - jeruki
About the database direct access its about the same as the user will never see the wp login only the drupal form, so in the end I will have to make a POST to WP to have it pull the user data from the database and do the login, but what really says if you are authenticated is the cookie so it will be generated and returned in the same way, only that the process to generate it will be different, I already tried this approach. - jeruki
did you set the $cookie_domain in settings.php in Drupal. if this is different from the cookie domain set by WP. you could have a problem. Cookie domain for WP is set in options table in database - Funky Dude
It is working now but just in case I checked, the drupal cookie_domain is there but I could not find the record in the WP database. - jeruki

1 Answers

0
votes

I realised there was an error with the regular expression from the forumnone site as it was cutting off the first letter of the wordpress cookies names (they appeared as ordpress_ and ordpress_logged_in) and also the if condition to the detect the wordpress cookie was not supposed to execute in !== false but if the comparison was === false.

After fixing these issues it was not necessary to ignore the drupal session cookie, just append it.

The working code for the get cookies from headers loop is:

// get cookies from headers
        if (200 == $response->code) {
            foreach ($response->headers as $key => $value) {

                if ($key == 'set-cookie') {

                    $cookies = preg_split('/(?<=\S),(?=\S)/', $value);

                    foreach ($cookies as $cookie) {
                        // is not wordpres cookie, concatenate
                        if(strpos($cookie, 'wordpress') === false) {
                            drupal_add_http_header($key, $cookie);
                        } else {
                            // is wordpress, send separately since drupal_add_http_header concatenates with commas
                            header($key . ': ' . $cookie, FALSE);
                        }
                    }
                }

            }

        } elseif(isset($response->error)) {
            // report any errors
            drupal_set_message(t($response->data), 'error');
        }