XF 2.0 Authenticate user using XF database with java client/server program.

Andrew

Well-known member
Hello all.

I have a client/server program and I would like to authenticate users who are registered on my forum. I did some digging and wrote some code but my password hash is coming up different than the one stored in the database. Using the xenforo system as my base user system would allow me to integrate cool features from my program into the forum while also saving me the trouble of creating registration, password recovery, email change, ban, upgrade, and other systems.

What I know right now.
  1. I can "emulate" or translate the xenforo code into java code and give that a shot. However I have tried this and my password's don't match up, yet.
  2. Another method is to invoke a php script from java which would save a lot of effort (especially if xenforo changes password hash code).
What are your thoughts on how you may handle this?
 
We’re almost certainly going to be sticking with the PHP default built in authentication methods which use password_create and password_verify functions. The default algorithm supported by these currently is BCrypt.

It is possible to support BCrypt in Java. See the following example code:

https://gist.github.com/craSH/5217757
 
Another option would be to create a custom password hashing function, one that can also be replicated in java.

This is strongly not recommended, I'm only bringing it up for the sake of completeness.


Fillip
 
Thanks for the replies guys!

I tried the code @Chris D posted. However I am still getting a different hash.

Check out what I have noticed.

Password: password
Foro Hash: $2y$10$lmgN7WV2//A.TA83N/K.EeGMwdh6h3HvaZm6ajxReLzqnPBS8a4T6
Java Hash: $2a$10$794NXyoVYG7Q3bm2dR3Fwe27febZ.n8UAwiYfMyasbjIm2fCoUCEm

Right off the bat I noticed that XF generates the hash starting with $2y and the java version starts off with $2a.
Also having a look in xf_user_authenticate.bin we have this line.

Code:
a:1:{s:4:"hash";s:60:"$2y$10$lmgN7WV2//A.TA83N/K.EeGMwdh6h3HvaZm6ajxReLzqnPBS8a4T6";}

Does
a:1:{s:4:"hash";s:60:
have something to do with setting up the rest of the password hash? What am I missing here? :unsure:
 
$2y and $2a identifies that the hash was generated with BCrypt - they should be backwards compatible and both represent a valid BCrypt hash.

The data is stored as a serialised string which is a PHP method which allows an array to be converted to a string which represents that array and be converted back from a string to an array.

Looks like you can use this to emulate that in Java:

https://github.com/kayahr/pherialize
 
I forgot to post the code from my test earlier. Here it is now.

https://pastebin.com/JhX9MhS3

I will have a look at the serialization method you had posted. Thank you for letting me pick your brain. I will see what I can come up with and I'll post back :)
 
Code:
<?php
$ip = $_POST['ip'];
$name = $_POST['name'];
$pass = $_POST['pass'];
$email = $_POST['email'];
if (!isset($ip, $name, $pass)) {
       die();
}
/**
* Create the bridge to Xenforo
**/
$fileDir = '../../';
require($fileDir . '/src/XF.php');
XF::start($fileDir);

/**
* Check if the username is registered (login and prompt for e-mail to create account)
*/
$finder = \XF::finder('XF:User');
$user = $finder->where('username', $name)->fetchOne();
if(!$user) {
    /**
     * Validate the username before proceeding with registration
     */
    $validator = \XF::app()->validator('Username');
    $username = $validator->coerceValue($name);
    if (!$validator->isValid($username, $errorKey)) {
        if($errorKey == 'too_long') {
            die('Username too long.');
        } else if($errorKey == 'disallowed' || $errorKey == 'censored') {
            die('Username contained disallowed words.');
        } else if($errorKey == 'regex' || $errorKey == 'comma') {
            die('Username contains incorrect characters.');
        } else if($errorKey == 'duplicate') {
            die('Username must be unique.');
        }
    }

    /**
     * If the account isn't registered and POST doesn't include email.. request! :-)
     */
    if(empty($email)) {
        die('Email required.');
    }

    /**
     * Ensure email isn't in use
     */
    $emailInUse = \XF::finder('XF:User')->where('email', $email)->fetchOne();
    if ($emailInUse) {
        die('Email in use.');
    }

    /**
     * Validate email
     */
    $emailValidator = \XF::app()->validator('Email');
    $emailValidator->setOption('check_typos', true);

    if (!$emailValidator->isValid($email, $errorKey)) {
        if ($errorKey == 'banned') {
            die('Email banned.');
        } else if ($errorKey == 'typo') {
            die('Email type.');
        } else {
            die('Invalid email.');
        }
    }

    /**
     * Register the new user
     */
    $registration = XF::service('XF:User\Registration');
    $input['username'] = $name;
    $input['email'] = $email;
    $input['password'] = $pass;
    $registration->setFromInput($input);
    $user = $registration->save();

    /**
     * Set the user groups
     */
    $user-> user_group_id = 2;
    //$user->secondary_group_ids = [1, 2, 3];
    $user->save();

    /**
     * Log the user in
     */
    $columns = array("user_id", "username", "user_group_id", "secondary_group_ids");
    $data = array();
    foreach ($columns as $c) {
        $data[$c] = $user[$c];
    }
    die('d=' . json_encode($data));
}

/**
* Check if the user is banned
*/
if($user->is_banned) {
    die('Banned.');
}

/**
* Verify the account details
**/
$loginService = \XF::app()->service('XF:User\Login', $name, $ip);
$success = $loginService->validate($pass);
if(!$success) {
    die('Incorrect password. Please try again.');
}

/**
* Successful login
**/
$columns = array("user_id", "username", "user_group_id", "secondary_group_ids");
$data = array();
foreach ($columns as $c) {
    $data[$c] = $user[$c];
}
die('d=' . json_encode($data));
 
Back
Top Bottom