WordPress authentication in node.js

What is the best way to allow a user to use a node.js app using their user account from a WordPress page?

I have tried storing session information in Redis, but I am not very familiar with PHP and have run into a dead end where the session isn’t being stored. I have used this guide.

Read More

I have also tried using node-phpass but it is not very well documented. I have successfully connected to the WordPress database, but the password hashes I generate with node-phpass do not match the hashes in the wp_users table.

This is what I’m using to test node-phpass:

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'wordpress',
  password : 'jPWrwvGbYXMADd8F',
  database : 'wordpress',
});

connection.query('SELECT * FROM wp_users', function(err, rows) {
    if (err) { 
        console.log("Database error: " + err);
    } else {
         for (var i = 0; i < rows.length; i++){
            console.log("Rows[" + i + "] : " + rows[i].user_login + " " + passwordHash.checkPassword('secret',rows[i].user_pass));
         }
    }
});

Aside from sharing the session, are there any other better ways to do this? OAuth?

Related posts

Leave a Reply

4 comments

  1. I didn’t quite understand if you want to the user authenticate primarily on your node.js application and then creates the cookie for WordPress, or the opposite, but once you understand how WordPress are doing it, you’ll be able to accomplish both.

    First of all, WordPress use lots of hashes and salts to security its cookies, so you need to know where these hashes and salts are to be able to define the same values for those on you nodejs application.

    On wp-config.php you will find the constants of the hashes and salts we’re looking for, I think you just need to transport to your nodejs application the constant values for LOGGED_IN_KEY, LOGGED_IN_SALT, NONCE_KEY and NONCE_SALT. You might give a better look on the file wp-includes/default-constants.php where WordPress keep its default constant values, pay attention to the if(defined(..)) because if the constant is defined in somewhere else prior to this file (probably on the wp-config.php) the value will be from that not from the wp-includes/default-constants.php file.

    Now, you need to understand how WordPress is creating its cookies.
    This is an example of logged in cookie:

    Cookie Name: wordpress_logged_in_2801795354f6f1c2d2ab3b48033a8257
    Cookie Value: admin|1392386298|647852d61caf4cfdea975d54ecb09ca8
    Cookie Value: %user_login%|%cookie_expire_time%|%hashed_sliced_user_hashed_password%
    

    You need to make your nodejs application understand this cookie, being able to break it just like WordPress.

    The functions to create authentication cookies on WordPress resides on wp-includes/pluggable.php

    $logged_in_cookie = wp_generate_auth_cookie($user_id, $expiration, 'logged_in');
    //...
    setcookie(LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true);
    

    Note that the constant LOGGED_IN_COOKIE is default set on wp-includes/default-constants.php, on the same file you have the COOKIEHASH constant, you need to transport the value of that to your nodejs application to be able to read and validate the cookie name too, the COOKIEHASH is just the website URL hashed with md5.

    if ( !defined( 'COOKIEHASH' ) ) {
        $siteurl = get_site_option( 'siteurl' );
        if ( $siteurl )
            define( 'COOKIEHASH', md5( $siteurl ) );
        else
            define( 'COOKIEHASH', '' );
    }
    //...
    define('LOGGED_IN_COOKIE', 'wordpress_logged_in_' . COOKIEHASH);
    

    if you don’t want to transport all that, you can just set on wp-config.php the LOGGED_IN_COOKIE constant with the logged in cookie name that you want for WordPress and portrait just this constant to your nodejs, like this:

    //WordPress wp-config.php
    define('LOGGED_IN_COOKIE', 'logged_in_'.'%privateHashKey%');
    //NODEJS
    var LOGGED_IN_COOKIE = 'logged_in_'+'%privateHashKey%';
    

    And finally you’ll need to portrait the the functions wp_generate_auth_cookie, wp_hash and hash_hmacto your nodejs application if you want to create the cookie there, the first two functions reside on the filewp-inclues/pluggable.phpthehash_hmacresides onwp-includes/compat.php`.

    if ( !function_exists('wp_generate_auth_cookie') ) :
    //...
    function wp_generate_auth_cookie($user_id, $expiration, $scheme = 'auth') {
        $user = get_userdata($user_id);
    
        $pass_frag = substr($user->user_pass, 8, 4);
    
        $key = wp_hash($user->user_login . $pass_frag . '|' . $expiration, $scheme);
        $hash = hash_hmac('md5', $user->user_login . '|' . $expiration, $key);
    
        $cookie = $user->user_login . '|' . $expiration . '|' . $hash;
    
        return apply_filters('auth_cookie', $cookie, $user_id, $expiration, $scheme);
    }
    endif;
    

    Once you understand what wp_generate_auth_cookie function is doing, you’ll also be able to decode the cookie and authenticate the WordPress user on your nodejs application.

    Note that what WordPress is doing for the the logged in cookie value, is simply slicing the hashed password for the user on your database, merging with the user login name and the expiration time (unix time) for the cookie, and hashing all together. Then he creates the cookie value with user login name, the expiration time for the cookie and the hash that he just created, with the first two values he can verify the “token hash” with the hashed password from the user.

    You might want to know that to logout an user on WordPress you need to pass the nonce key for the user on the logout url, so if you want to logout an user from WordPress by your nodejs application, you might want to transport the nonce key functions.

    If your application will be in a subdomain, you might want to declare on wp-config.php, or WordPress will just use the full domain on the cookie, and the cookie will not be available for subdomains.

    define('COOKIE_DOMAIN', '.domain.com');
    

    Hope this give you better directions. Good luck.