Jump to content

Xenforo Hashing Style


Dretax

Recommended Posts

I was thinking on using XenForo instead of SMF. I have been using Xenforo most of the time anyway. I'm trying to build up the same hashing style at the login part, in MTA, but I'm kinda lost. Is anyone here familiar with XenForo? I'm unsure where could I find the salt, or which one is the salt in the db... As I see It hashes the given (password + randomgeneratedstringfromthedb) and then hashes It again? I'm not a PHP expert, sorry for that, but I'm trying to reproduce the same thing in Lua. Anyone could tell me where is, or which one is the salt string in the db?

 

    /**
	 * Default Salt Length
	 *
	 * @var integer
	 */
	const DEFAULT_SALT_LENGTH = 30;

    public static function generateSalt($length = null)
	{
		if (!$length)
		{
			$length = self::DEFAULT_SALT_LENGTH;
		}

		return XenForo_Application::generateRandomString($length);
	}


   	protected function _createHash($data)
	{
		$this->_setupHash();
		switch ($this->_hashFunc)
		{
			case 'sha256':
				return hash('sha256', $data);
			case 'sha1':
				return sha1($data);
			default:
				throw new XenForo_Exception("Unknown hash type");
		}
	}

	protected function _newPassword($password, $salt)
	{
		$hash = $this->_createHash($this->_createHash($password) . $salt);
		return array('hash' => $hash, 'salt' => $salt, 'hashFunc' => $this->_hashFunc);
	}

 

Link to comment

It looks like there is a setting for what hash function to use, depending on that the script will use sha256 or sha1.
The hashing would look like this in LUA, given you use sha256:

function hashPassword( password, salt )
	return sha256( sha256(password)..salt )
end

The hash itself is just a random string, as I don't have any experience with XenForo I can't tell you where or how it's stored.

Link to comment
2 minutes ago, 3aGl3 said:

It looks like there is a setting for what hash function to use, depending on that the script will use sha256 or sha1.
The hashing would look like this in LUA, given you use sha256:


function hashPassword( password, salt )
	return sha256( sha256(password)..salt )
end

The hash itself is just a random string, as I don't have any experience with XenForo I can't tell you where or how it's stored.

Alright, so as I thought It's like that. I will see If I can look It up, since older posts on the Xenforo page doesn't really tell anything.

I will also wait, somebody may have a solution. Thanks.

Link to comment
19 minutes ago, 3aGl3 said:

If you could give me more of the PHP I can probably tell you.
Is there something like a register function of some sort?

Uhh. No idea,  this is in the registration php.

 

<?php

/**
 * Controller for registration-related actions.
 *
 * @package XenForo_Users
 */
class XenForo_ControllerPublic_Register extends XenForo_ControllerPublic_Abstract
{
	protected function _preDispatch($action)
	{
		// prevent discouraged IP addresses from registering
		if (XenForo_Application::get('options')->preventDiscouragedRegistration && $this->_isDiscouraged())
		{
			throw $this->responseException($this->responseError(
				new XenForo_Phrase('new_registrations_currently_not_being_accepted')
			));
		}
	}

	/**
	 * Displays a form to register a new user.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionIndex()
	{
		if (XenForo_Visitor::getUserId())
		{
			throw $this->responseException(
				$this->responseRedirect(
					XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
					$this->getDynamicRedirect()
				)
			);
		}

		$this->_assertRegistrationActive();

		$username = '';
		$email = '';

		if ($login = $this->_input->filterSingle('login', XenForo_Input::STRING))
		{
			if (XenForo_Helper_Email::isEmailValid($login))
			{
				$email = $login;
			}
			else
			{
				$username = $login;
			}
		}

		$fields = array(
			'username' => $username,
			'email' => $email
		);

		$writer = XenForo_DataWriter::create('XenForo_DataWriter_User');
		if ($username !== '')
		{
			$writer->set('username', $username);
		}
		if ($email !== '')
		{
			$writer->set('email', $email);
		}

		return $this->_getRegisterFormResponse($fields, $writer->getErrors());
	}

	protected function _getRegisterFormResponse(array $fields, array $errors = array())
	{
		$options = XenForo_Application::get('options');

		if (empty($fields['timezone']))
		{
			$fields['timezone'] = $options->guestTimeZone;
			$fields['timezoneAuto'] = true;
		}

		if (!empty($fields['custom_fields']) && is_array($fields['custom_fields']))
		{
			$customFieldValues = $fields['custom_fields'];
		}
		else
		{
			$customFieldValues = array();
		}

		XenForo_Application::getSession()->set('registrationTime', time());

		$regKey = md5(uniqid('xf', true));
		XenForo_Application::getSession()->set('registrationKey', $regKey);

		$customFields = $this->_getFieldModel()->prepareUserFields(
			$this->_getFieldModel()->getUserFields(array('registration' => true)),
			true,
			$customFieldValues,
			false
		);

		$viewParams = array(
			'fields' => $fields,
			'errors' => $errors,

			'timeZones' => XenForo_Helper_TimeZone::getTimeZones(),
			'dobRequired' => $options->get('registrationSetup', 'requireDob'),

			'captcha' => XenForo_Captcha_Abstract::createDefault(),
			'tosUrl' => XenForo_Dependencies_Public::getTosUrl(),

			'regKey' => $regKey,
			'fieldMap' => $this->_getFieldHashMap(),

			'customFields' => $customFields,
			'customFieldHoneyPot' => $this->_getCustomFieldHoneyPot($customFields)
		);

		return $this->responseView(
			'XenForo_ViewPublic_Register_Form',
			'register_form',
			$viewParams,
			$this->_getRegistrationContainerParams()
		);
	}

	/**
	 * Validate a single field
	 *
	 * @return XenForo_ControllerResponse_Redirect
	 */
	public function actionValidateField()
	{
		$this->_assertPostOnly();

		$field = $this->_getFieldValidationInputParams();
		$inputName = $field['name'];
		$fieldMap = $this->_getFieldHashMap();
		$doValidate = true;

		foreach ($fieldMap AS $name => $hashedName)
		{
			if ($field['name'] == $hashedName)
			{
				$field['name'] = $name;
				$doValidate = $this->_hashableFields[$name];
				break;
			}
		}

		$writer = XenForo_DataWriter::create('XenForo_DataWriter_User');

		if ($doValidate)
		{
			if (preg_match('/^custom_field_([a-zA-Z0-9_]+)$/', $field['name'], $match))
			{
				$writer->setCustomFields(array($match[1] => $field['value']));
			}
			else
			{
				$writer->set($field['name'], $field['value']);
			}
		}

		if ($errors = $writer->getErrors())
		{
			foreach ($errors AS $key => $value)
			{
				if ($key === $field['name'])
				{
					unset($errors[$key]);
					$errors[$inputName] = $value;
				}
			}
			return $this->responseError($errors);
		}

		return $this->responseRedirect(
			XenForo_ControllerResponse_Redirect::SUCCESS,
			'',
			new XenForo_Phrase('redirect_field_validated', array('name' => $inputName, 'value' => $field['value']))
		);
	}

	/**
	 * Fields that get hashed names. Key is field, value is true if included in part of
	 * DW data directly. (False otherwise.)
	 *
	 * @var array
	 */
	protected $_hashableFields = array(
		'username' => true,
		'username_hp' => false,
		'email' => true,
		'email_hp' => false,
		'gender' => true,
		'timezone' => true,
		'password' => false,
		'password_hp' => false,
		'password_confirm' => false,
		'password_confirm_hp' => false,
		'custom_fields' => false
	);

	protected function _getFieldHashMap($secretKey = null)
	{
		if (!$secretKey)
		{
			$session = XenForo_Application::getSession();

			$secretKey = hash_hmac('md5',
				$session->isRegistered('registrationKey') ? $session->get('registrationKey') : XenForo_Application::$time,
				XenForo_Application::getConfig()->globalSalt
			);
		}

		$map = array();
		foreach ($this->_hashableFields AS $field => $null)
		{
			$map[$field] = hash_hmac('md5', $field, $secretKey);
		}

		return $map;
	}

	protected function _getCustomFieldHoneyPot(array $customFields)
	{
		if ($customFields)
		{
			$field = reset($customFields);
		}
		else
		{
			$session = XenForo_Application::getSession();
			$key = $session->isRegistered('registrationKey') ? $session->get('registrationKey') : strval(XenForo_Application::$time);

			$field = array(
				'field_id' => substr($key, 0, hexdec($key[0]) + 1),
				'title' => new XenForo_Phrase('verification')
			);
		}

		$field['field_id'] = strtolower(rtrim(strtr(base64_encode($field['field_id']), '+/', '__'), '='));
		$field['field_type'] = 'textbox';
		$field['field_value'] = '';
		$field['required'] = 0;
		$field['show_registration'] = 1;
		$field['user_editable'] = 1;
		$field['display_template'] = '';
		$field['isEditable'] = true;
		$field['showRegistration'] = true;
		$field['description'] = new XenForo_Phrase('please_leave_this_field_blank');

		return $field;
	}

	protected function _getRegistrationInputData()
	{
		$errors = array();
		$fieldMap = $this->_getFieldHashMap();

		$data = $this->_input->filter(array(
			'username'   => XenForo_Input::STRING,
			'email'      => XenForo_Input::STRING,
			'timezone'   => XenForo_Input::STRING,
			'gender'     => XenForo_Input::STRING,
			'location'   => XenForo_Input::STRING,
			'dob_day'    => XenForo_Input::UINT,
			'dob_month'  => XenForo_Input::UINT,
			'dob_year'   => XenForo_Input::UINT,
		));

		$passwords = array(
			'password' => $this->_input->filterSingle($fieldMap['password'], XenForo_Input::STRING),
			'password_confirm' => $this->_input->filterSingle($fieldMap['password_confirm'], XenForo_Input::STRING)
		);

		foreach ($fieldMap AS $fieldName => $hashedName)
		{
			if ($this->_hashableFields[$fieldName])
			{
				if (strlen($data[$fieldName]))
				{
					$errors['field_hash'] = new XenForo_Phrase('some_fields_contained_unexpected_data_try_again');
				}

				$data[$fieldName] = $this->_input->filterSingle($hashedName, XenForo_Input::STRING);
			}
			else if (substr($fieldName, -3) == '_hp')
			{
				if (strlen($this->_input->filterSingle($hashedName, XenForo_Input::STRING)))
				{
					$errors['field_hash'] = new XenForo_Phrase('some_fields_contained_unexpected_data_try_again');
				}
			}
		}

		$customFields = $this->_input->filterSingle($fieldMap['custom_fields'], XenForo_Input::ARRAY_SIMPLE);
		$customFieldsShown = $this->_getFieldModel()->getUserFields(array('registration' => true));

		$customFieldHoneyPot = $this->_getCustomFieldHoneyPot($customFieldsShown);
		if ($customFieldHoneyPot
			&& isset($customFields[$customFieldHoneyPot['field_id']])
			&& $customFields[$customFieldHoneyPot['field_id']] !== ''
		)
		{
			$errors['field_hash'] = new XenForo_Phrase('some_fields_contained_unexpected_data_try_again');
		}

		// bit of a hack, but ensures HP password fields don't get logged
		unset($_POST[$fieldMap['password']], $_POST[$fieldMap['password_confirm']]);

		return array(
			'data' => $data,
			'passwords' => $passwords,
			'customFields' => $customFields,
			'customFieldsShown' => array_keys($customFieldsShown),
			'errors' => $errors
		);
	}

	protected $_regInputData;

	protected function _getRegistrationInputDataSafe()
	{
		if (!$this->_regInputData)
		{
			$this->_regInputData = $this->_getRegistrationInputData();
		}

		return $this->_regInputData;
	}

	/**
	 * Registers a new user.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionRegister()
	{
		$this->_assertPostOnly();
		$this->_assertRegistrationActive();

		$inputData = $this->_getRegistrationInputDataSafe();
		$data = $inputData['data'];
		$passwords = $inputData['passwords'];
		$customFields = $inputData['customFields'];
		$customFieldsShown = $inputData['customFieldsShown'];
		$errors = $inputData['errors'];

		$options = XenForo_Application::getOptions();

		if (!XenForo_Captcha_Abstract::validateDefault($this->_input))
		{
			$errors[] = new XenForo_Phrase('did_not_complete_the_captcha_verification_properly');
		}

		if (XenForo_Dependencies_Public::getTosUrl() && !$this->_input->filterSingle('agree', XenForo_Input::UINT))
		{
			$errors[] = new XenForo_Phrase('you_must_agree_to_terms_of_service');
		}

		$writer = XenForo_DataWriter::create('XenForo_DataWriter_User');
		if ($options->registrationDefaults)
		{
			$writer->bulkSet($options->registrationDefaults, array('ignoreInvalidFields' => true));
		}
		$writer->bulkSet($data);
		$writer->setPassword($passwords['password'], $passwords['password_confirm'], null, true);

		// if the email corresponds to an existing Gravatar, use it
		if ($this->_canApplyAvatar() && $options->gravatarEnable && XenForo_Model_Avatar::gravatarExists($data['email']))
		{
			$writer->set('gravatar', $data['email']);
		}

		$writer->set('user_group_id', XenForo_Model_User::$defaultRegisteredGroupId);
		$writer->set('language_id', XenForo_Visitor::getInstance()->get('language_id'));

		$writer->setCustomFields($customFields, $customFieldsShown);

		if (!$this->_validateBirthdayInput($writer, $birthdayError))
		{
			$errors[] = $birthdayError;
		}

		$registerTime = XenForo_Application::getSession()->get('registrationTime');
		if (!$registerTime || ($registerTime + $options->get('registrationTimer')) > time())
		{
			$errors[] = new XenForo_Phrase('sorry_you_must_wait_longer_to_create_account');
		}

		$regKey = XenForo_Application::getSession()->get('registrationKey');
		if (!$regKey  || $regKey != $this->_input->filterSingle('reg_key', XenForo_Input::STRING))
		{
			$errors[] = new XenForo_Phrase('something_went_wrong_please_try_again');
		}

		$spamModel = $this->_runSpamCheck($writer, $errors);

		$writer->advanceRegistrationUserState();
		$writer->preSave();

		$errors = array_merge($errors, $writer->getErrors());

		if ($errors)
		{
			$fields = $data;
			$fields['tos'] = $this->_input->filterSingle('agree', XenForo_Input::UINT);
			$fields['custom_fields'] = $customFields;
			return $this->_getRegisterFormResponse($fields, $errors);
		}

		$writer->save();

		$user = $writer->getMergedData();

		$spamModel->logSpamTrigger('user', $user['user_id']);

		if ($user['user_state'] == 'email_confirm')
		{
			$this->_getUserConfirmationModel()->sendEmailConfirmation($user);
		}

		return $this->_completeRegistration($user);
	}

	protected function _completeRegistration(array $user, array $extraParams = array())
	{
		XenForo_Model_Ip::log($user['user_id'], 'user', $user['user_id'], 'register');

		$userModel = $this->_getUserModel();
		$userModel->sendWelcomeContact($user);

		$visitor = XenForo_Visitor::setup($user['user_id']);
		XenForo_Application::getSession()->userLogin($user['user_id'], $visitor['password_date']);

		$this->_executePromotionUpdate(true);
		$this->_executeTrophyUpdate(true);

		// keep the user logged in for a while - more friendly for new users
		$userModel->setUserRememberCookie($user['user_id']);

		$redirect = $this->_input->filterSingle('redirect', XenForo_Input::STRING);

		$visitor = XenForo_Visitor::getInstance();
		$viewParams = $extraParams + array(
			'user' => $visitor->toArray(),
			'canEditProfile' => $visitor->canEditProfile(),
			'redirect' => ($redirect ? XenForo_Link::convertUriToAbsoluteUri($redirect) : '')
		);

		return $this->responseView(
			'XenForo_ViewPublic_Register_Process',
			'register_process',
			$viewParams,
			$this->_getRegistrationContainerParams()
		);
	}

	/**
	 * Displays a form to join using Facebook or logs in an existing account.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionFacebook()
	{
		$assocUserId = $this->_input->filterSingle('assoc', XenForo_Input::UINT);
		$redirect = $this->_getExternalAuthRedirect();

		$fbRedirectUri = XenForo_Link::buildPublicLink('canonical:register/facebook', false, array(
			'assoc' => ($assocUserId ? $assocUserId : false)
		));

		if ($this->_input->filterSingle('reg', XenForo_Input::UINT))
		{
			XenForo_Application::getSession()->set('loginRedirect', $redirect);
			XenForo_Application::getSession()->remove('fbToken');

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
				XenForo_Helper_Facebook::getFacebookRequestUrl($fbRedirectUri)
			);
		}

		$fbToken = $this->_input->filterSingle('t', XenForo_Input::STRING);
		if (!$fbToken)
		{
			$fbToken = XenForo_Application::getSession()->get('fbToken');
		}

		if (!$fbToken)
		{
			$error = $this->_input->filterSingle('error', XenForo_Input::STRING);
			if ($error == 'access_denied')
			{
				return $this->responseError(new XenForo_Phrase('you_did_not_grant_permission_to_access_external_account'));
			}

			$code = $this->_input->filterSingle('code', XenForo_Input::STRING);
			if (!$code)
			{
				return $this->responseError(new XenForo_Phrase('error_occurred_while_connecting_with_facebook'));
			}

			$state = $this->_input->filterSingle('state', XenForo_Input::STRING);
			$session = XenForo_Application::getSession();
			if (!$state || !$session->get('fbCsrfState') || $state !== $session->get('fbCsrfState'))
			{
				return $this->responseRedirect(
					XenForo_ControllerResponse_Redirect::SUCCESS,
					XenForo_Link::buildPublicLink('canonical:index')
				);
			}

			$token = XenForo_Helper_Facebook::getAccessTokenFromCode($code, $fbRedirectUri);
			$fbError = XenForo_Helper_Facebook::getFacebookRequestErrorInfo($token, 'access_token');
			if ($fbError)
			{
				return $this->responseError(new XenForo_Phrase('error_occurred_while_connecting_with_facebook'));
			}

			$fbToken = $token['access_token'];
		}

		$fbUser = XenForo_Helper_Facebook::getUserInfo($fbToken);
		$fbError = XenForo_Helper_Facebook::getFacebookRequestErrorInfo($fbUser, 'id');
		if ($fbError)
		{
			return $this->responseError(new XenForo_Phrase('error_occurred_while_connecting_with_facebook'));
		}

		$userModel = $this->_getUserModel();
		$userExternalModel = $this->_getUserExternalModel();

		$fbAssoc = $userExternalModel->getExternalAuthAssociation('facebook', $fbUser['id']);
		if ($fbAssoc && $userModel->getUserById($fbAssoc['user_id']))
		{
			$userExternalModel->updateExternalAuthAssociationExtra(
				$fbAssoc['user_id'], 'facebook', array('token' => $fbToken)
			);

			$redirect = XenForo_Application::getSession()->get('loginRedirect');
			if (!$redirect)
			{
				$redirect = $this->getDynamicRedirect(false, false);
			}

			XenForo_Helper_Facebook::setUidCookie($fbUser['id']);

			/** @var XenForo_ControllerHelper_Login $loginHelper */
			$loginHelper = $this->getHelper('Login');
			$loginHelper->tfaRedirectIfRequiredPublic($fbAssoc['user_id'], $redirect, true);

			$visitor = XenForo_Visitor::setup($fbAssoc['user_id']);
			XenForo_Application::getSession()->userLogin($fbAssoc['user_id'], $visitor['password_date']);

			$this->_getUserModel()->setUserRememberCookie($fbAssoc['user_id']);

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				$redirect
			);
		}

		XenForo_Helper_Facebook::setUidCookie(0);

		parent::_assertBoardActive('facebook');

		if (empty($fbUser['email']))
		{
			return $this->responseError(new XenForo_Phrase('facebook_did_not_provide_email'));
		}

		$existingUser = false;
		$emailMatch = false;
		if (XenForo_Visitor::getUserId())
		{
			$existingUser = XenForo_Visitor::getInstance();
		}
		else if ($assocUserId)
		{
			$existingUser = $userModel->getUserById($assocUserId);
		}

		if (!$existingUser)
		{
			$existingUser = $userModel->getUserByEmail($fbUser['email']);
			$emailMatch = true;
		}

		$viewName = 'XenForo_ViewPublic_Register_Facebook';
		$templateName = 'register_facebook';

		XenForo_Application::getSession()->set('fbToken', $fbToken);

		if ($existingUser)
		{
			// must associate: matching user
			return $this->_getExternalRegisterFormResponse($viewName, $templateName, array(
				'associateOnly' => true,

				'fbUser' => $fbUser,

				'existingUser' => $existingUser,
				'emailMatch' => $emailMatch,
				'redirect' => $redirect
			));
		}

		$this->_assertRegistrationActive();

		if (!empty($fbUser['birthday']))
		{
			$this->_validateBirthdayString($fbUser['birthday'], 'm/d/y');
		}

		return $this->_getExternalRegisterFormResponse($viewName, $templateName, array(
			'fbUser' => $fbUser,
			'redirect' => $redirect,
			'showDob' => empty($fbUser['birthday'])
		));
	}

	/**
	 * Registers a new account (or associates with an existing one) using Facebook.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionFacebookRegister()
	{
		$this->_assertPostOnly();

		$fbToken = XenForo_Application::getSession()->get('fbToken');

		$fbUser = XenForo_Helper_Facebook::getUserInfo($fbToken);
		if (empty($fbUser['id']))
		{
			return $this->responseError(new XenForo_Phrase('error_occurred_while_connecting_with_facebook'));
		}

		if (empty($fbUser['email']))
		{
			return $this->responseError(new XenForo_Phrase('facebook_did_not_provide_email'));
		}

		$userExternalModel = $this->_getUserExternalModel();

		$redirect = XenForo_Application::getSession()->get('loginRedirect');
		if (!$redirect)
		{
			$redirect = $this->getDynamicRedirect(false, false);
		}

		$doAssoc = ($this->_input->filterSingle('associate', XenForo_Input::STRING)
			|| $this->_input->filterSingle('force_assoc', XenForo_Input::UINT)
		);

		if ($doAssoc)
		{
			$userId = $this->_associateExternalAccount();

			$userExternalModel->updateExternalAuthAssociation(
				'facebook', $fbUser['id'], $userId, array('token' => $fbToken)
			);
			XenForo_Helper_Facebook::setUidCookie($fbUser['id']);

			XenForo_Application::getSession()->remove('loginRedirect');
			XenForo_Application::getSession()->remove('fbToken');

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				$redirect
			);
		}

		$data = $this->_input->filter(array(
			'username'   => XenForo_Input::STRING,
			'timezone'   => XenForo_Input::STRING,
			'location'   => XenForo_Input::STRING,
			'dob_day'    => XenForo_Input::UINT,
			'dob_month'  => XenForo_Input::UINT,
			'dob_year'   => XenForo_Input::UINT
		));

		if (isset($fbUser['gender']))
		{
			switch ($fbUser['gender'])
			{
				case 'man':
				case 'male':
					$data['gender'] = 'male';
					break;

				case 'woman':
				case 'female':
					$data['gender'] = 'female';
					break;
			}
		}

		if (!empty($fbUser['birthday']))
		{
			$birthday = $this->_validateBirthdayString($fbUser['birthday'], 'm/d/y');
			if ($birthday)
			{
				$data['dob_year'] = $birthday[0];
				$data['dob_month'] = $birthday[1];
				$data['dob_day'] = $birthday[2];
			}
		}

		if (!empty($fbUser['website']))
		{
			list($website) = preg_split('/\r?\n/', $fbUser['website']);
			if ($website && Zend_Uri::check($website))
			{
				$data['homepage'] = $website;
			}
		}

		$data['email'] = $fbUser['email'];

		if (!empty($fbUser['location']['name']))
		{
			$data['location'] = $fbUser['location']['name'];
		}

		$writer = $this->_setupExternalUser($data);
		if (!$this->_validateBirthdayInput($writer, $birthdayError))
		{
			$writer->error($birthdayError);
		}

		$spamModel = $this->_runSpamCheck($writer);

		$writer->advanceRegistrationUserState(false);
		$writer->save();
		$user = $writer->getMergedData();

		$spamModel->logSpamTrigger('user', $user['user_id']);

		if ($this->_canApplyAvatar())
		{
			$avatarData = XenForo_Helper_Facebook::getUserPicture($fbToken);
			$this->_applyAvatar($user, $avatarData);
		}

		$userExternalModel->updateExternalAuthAssociation(
			'facebook', $fbUser['id'], $user['user_id'], array('token' => $fbToken)
		);
		XenForo_Helper_Facebook::setUidCookie($fbUser['id']);

		XenForo_Application::getSession()->remove('loginRedirect');
		XenForo_Application::getSession()->remove('fbToken');

		return $this->_completeRegistration($user, array('redirect' => $redirect));
	}

	public function actionTwitter()
	{
		$assocUserId = $this->_input->filterSingle('assoc', XenForo_Input::UINT);
		$oauth = XenForo_Helper_Twitter::getOauthConsumer(
			XenForo_Link::buildPublicLink('canonical:register/twitter', null, array(
				'assoc' => ($assocUserId ? $assocUserId : false)
			))
		);
		if (!$oauth)
		{
			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
				$this->getDynamicRedirect()
			);
		}

		$session = XenForo_Application::getSession();
		$redirect = $this->_getExternalAuthRedirect();

		if ($this->_input->filterSingle('reg', XenForo_Input::UINT))
		{
			XenForo_Application::getSession()->set('loginRedirect', $redirect);

			try
			{
				$requestToken = $oauth->getRequestToken();
			}
			catch (Zend_Oauth_Exception $e)
			{
				return $this->responseError(new XenForo_Phrase('unexpected_error_occurred'));
			}

			$session->set('twitterRequestToken', serialize($requestToken));

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
				$oauth->getRedirectUrl()
			);
		}

		try
		{
			$requestToken = @unserialize($session->get('twitterRequestToken'));
			if ($requestToken)
			{
				if ($this->_input->filterSingle('denied', XenForo_Input::STRING))
				{
					return $this->responseError(new XenForo_Phrase('unexpected_error_occurred'));
				}

				$accessToken = $oauth->getAccessToken($this->_input->filter(array(
					'oauth_token' => XenForo_Input::STRING,
					'oauth_verifier' => XenForo_Input::STRING
				)), $requestToken);
			}
			else
			{
				$accessToken = @unserialize($session->get('twitterAccessToken'));
				if (!$accessToken)
				{
					return $this->responseError(new XenForo_Phrase('unexpected_error_occurred'));
				}
			}
		}
		catch (Zend_Service_Twitter_Exception $e)
		{
			return $this->responseError(new XenForo_Phrase('unexpected_error_occurred'));
		}

		$session->remove('twitterRequestToken');
		$session->set('twitterAccessToken', serialize($accessToken));

		$credentials = XenForo_Helper_Twitter::getUserFromToken($accessToken);
		if (!$credentials)
		{
			return $this->responseError(new XenForo_Phrase('unexpected_error_occurred'));
		}
		$userId = $credentials['id_str'];

		$userModel = $this->_getUserModel();
		$userExternalModel = $this->_getUserExternalModel();

		$redirect = XenForo_Application::getSession()->get('loginRedirect');

		$twitterAssoc = $userExternalModel->getExternalAuthAssociation('twitter', $userId);
		if ($twitterAssoc && $userModel->getUserById($twitterAssoc['user_id']))
		{
			$userExternalModel->updateExternalAuthAssociationExtra(
				$twitterAssoc['user_id'], 'twitter', array(
					'token' => $accessToken->getToken(),
					'secret' => $accessToken->getTokenSecret()
				)
			);

			/** @var XenForo_ControllerHelper_Login $loginHelper */
			$loginHelper = $this->getHelper('Login');
			$loginHelper->tfaRedirectIfRequiredPublic($twitterAssoc['user_id'], $redirect, true);

			$visitor = XenForo_Visitor::setup($twitterAssoc['user_id']);
			XenForo_Application::getSession()->userLogin($twitterAssoc['user_id'], $visitor['password_date']);

			$this->_getUserModel()->setUserRememberCookie($twitterAssoc['user_id']);

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				$redirect ? $redirect : $this->getDynamicRedirect(false, false)
			);
		}

		parent::_assertBoardActive('twitter');

		$session->set('twitterCredentials', serialize($credentials));

		$viewName = 'XenForo_ViewPublic_Register_Twitter';
		$templateName = 'register_twitter';

		$existingUser = XenForo_Visitor::getUserId() ? XenForo_Visitor::getInstance() : false;
		if ($existingUser)
		{
			// must associate: matching user
			return $this->_getExternalRegisterFormResponse($viewName, $templateName, array(
				'associateOnly' => true,
				'existingUser' => $existingUser,
				'redirect' => $redirect
			));
		}

		$this->_assertRegistrationActive();

		return $this->_getExternalRegisterFormResponse($viewName, $templateName, array(
			'redirect' => $redirect,
			'credentials' => $credentials
		));
	}

	/**
	 * Registers a new account (or associates with an existing one) using Twitter.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionTwitterRegister()
	{
		$this->_assertPostOnly();

		$session = XenForo_Application::getSession();
		$accessToken = @unserialize($session->get('twitterAccessToken'));
		$credentials = @unserialize($session->get('twitterCredentials'));

		if (!$accessToken || !$credentials)
		{
			return $this->responseError(new XenForo_Phrase('you_did_not_grant_permission_to_access_external_account'));
		}

		$userExternalModel = $this->_getUserExternalModel();

		$doAssoc = ($this->_input->filterSingle('associate', XenForo_Input::STRING)
			|| $this->_input->filterSingle('force_assoc', XenForo_Input::UINT)
		);

		$redirect = XenForo_Application::getSession()->get('loginRedirect');
		if (!$redirect)
		{
			$redirect = $this->getDynamicRedirect(false, false);
		}

		if ($doAssoc)
		{
			$userId = $this->_associateExternalAccount();

			$userExternalModel->updateExternalAuthAssociation(
				'twitter', $credentials['id_str'], $userId, array(
					'token' => $accessToken->getToken(),
					'secret' => $accessToken->getTokenSecret()
				)
			);

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				$redirect
			);
		}

		$options = XenForo_Application::getOptions();

		$data = $this->_input->filter(array(
			'username'   => XenForo_Input::STRING,
			'email'      => XenForo_Input::STRING,
			'timezone'   => XenForo_Input::STRING,
			'location'   => XenForo_Input::STRING,
			'dob_day'    => XenForo_Input::UINT,
			'dob_month'  => XenForo_Input::UINT,
			'dob_year'   => XenForo_Input::UINT,
		));
		if (!empty($credentials['location']))
		{
			$data['location'] = $credentials['location'];
		}

		if (!empty($credentials['entities']['url']['urls'][0]['expanded_url']))
		{
			$website = $credentials['entities']['url']['urls'][0]['expanded_url'];
			if (Zend_Uri::check($website))
			{
				$data['homepage'] = $website;
			}
		}

		$writer = $this->_setupExternalUser($data);

		if (!$this->_validateBirthdayInput($writer, $birthdayError))
		{
			$writer->error($birthdayError);
		}

		$spamModel = $this->_runSpamCheck($writer);

		$writer->advanceRegistrationUserState();
		$writer->save();
		$user = $writer->getMergedData();

		$spamModel->logSpamTrigger('user', $user['user_id']);

		if (!empty($credentials['profile_image_url']) && $this->_canApplyAvatar())
		{
			try
			{
				// get the original size
				$url = str_replace('_normal', '', $credentials['profile_image_url']);
				$request = XenForo_Helper_Http::getClient($url)->request();
				$avatarData = $request->getBody();
			}
			catch (Exception $e)
			{
				$avatarData = '';
			}
			$this->_applyAvatar($user, $avatarData);
		}

		$userExternalModel->updateExternalAuthAssociation(
			'twitter', $credentials['id_str'], $user['user_id'], array(
				'token' => $accessToken->getToken(),
				'secret' => $accessToken->getTokenSecret()
			)
		);

		if ($user['user_state'] == 'email_confirm')
		{
			$this->_getUserConfirmationModel()->sendEmailConfirmation($user);
		}

		return $this->_completeRegistration($user);
	}

	public function actionGoogle()
	{
		$code = $this->_input->filterSingle('code', XenForo_Input::STRING);
		$options = XenForo_Application::getOptions();
		$session = XenForo_Application::getSession();
		$redirect = $this->_getExternalAuthRedirect();

		if (!$options->googleClientId)
		{
			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::RESOURCE_CANONICAL,
				$this->getDynamicRedirect()
			);
		}

		$csrf = $this->_input->filterSingle('csrf', XenForo_Input::STRING);
		if ($csrf !== $session->get('sessionCsrf'))
		{
			return $this->responseError(new XenForo_Phrase('unexpected_error_occurred'));
		}

		$client = XenForo_Helper_Http::getClient('https://accounts.google.com/o/oauth2/token');
		$client->setParameterPost(array(
			'code' => $code,
			'client_id' => $options->googleClientId,
			'client_secret' => $options->googleClientSecret,
			'redirect_uri' => 'postmessage',
			'grant_type' => 'authorization_code'
		));
		$result = $client->request('POST');

		$body = @json_decode($result->getBody(), true);
		if (!$body || !empty($body['error']))
		{
			$credentials = $session->get('googleCredentials');
			if (!$credentials)
			{
				return $this->responseError(new XenForo_Phrase('error_occurred_when_connecting_to_google'));
			}
		}
		else
		{
			$idTokenParts = explode('.', $body['id_token']);

			$basicInfo = json_decode(base64_decode($idTokenParts[1]), true);
			if (!$basicInfo || empty($basicInfo['sub']))
			{
				return $this->responseError(new XenForo_Phrase('error_occurred_when_connecting_to_google'));
			}

			$credentials = array(
				'extra' => array(
					'access_token' => $body['access_token'],
					'expiry' => XenForo_Application::$time + $body['expires_in'],
					'refresh_token' => isset($body['refresh_token']) ? $body['refresh_token'] : null
				),
				'basic' => $basicInfo
			);
		}

		$basicInfo = $credentials['basic'];
		$userId = $basicInfo['sub'];

		$userModel = $this->_getUserModel();
		$userExternalModel = $this->_getUserExternalModel();

		$googleAssoc = $userExternalModel->getExternalAuthAssociation('google', $userId);
		if ($googleAssoc && $userModel->getUserById($googleAssoc['user_id']))
		{
			$existingExtra = unserialize($googleAssoc['extra_data']);
			if (!$credentials['extra']['refresh_token'] && !empty($existingExtra['refresh_token']))
			{
				$credentials['extra']['refresh_token'] = $existingExtra['refresh_token'];
			}

			$userExternalModel->updateExternalAuthAssociationExtra(
				$googleAssoc['user_id'], 'google', $credentials['extra']
			);

			/** @var XenForo_ControllerHelper_Login $loginHelper */
			$loginHelper = $this->getHelper('Login');
			$loginHelper->tfaRedirectIfRequiredPublic($googleAssoc['user_id'], $redirect, true);

			$visitor = XenForo_Visitor::setup($googleAssoc['user_id']);
			XenForo_Application::getSession()->userLogin($googleAssoc['user_id'], $visitor['password_date']);

			$this->_getUserModel()->setUserRememberCookie($googleAssoc['user_id']);

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				$redirect
			);
		}

		if (empty($basicInfo['email']) || empty($basicInfo['email_verified']) || $basicInfo['email_verified'] != 'true')
		{
			return $this->responseError(new XenForo_Phrase('you_must_have_verified_email_to_register_via_google'));
		}

		parent::_assertBoardActive('google');

		if (empty($credentials['user']))
		{
			$client = XenForo_Helper_Http::getClient('https://www.googleapis.com/plus/v1/people/me');
			$client->setParameterGet('access_token', $credentials['extra']['access_token']);
			$response = $client->request('GET');
			$userInfo = json_decode($response->getBody(), true);
			$credentials['user'] = $userInfo;
		}

		$session->set('googleCredentials', $credentials);

		$viewName = 'XenForo_ViewPublic_Register_Google';
		$templateName = 'register_google';

		$emailMatch = false;
		if (XenForo_Visitor::getUserId())
		{
			$existingUser = XenForo_Visitor::getInstance();
		}
		else
		{
			$existingUser = $userModel->getUserByEmail($basicInfo['email']);
			$emailMatch = (bool)$existingUser;
		}

		XenForo_Application::getSession()->set('loginRedirect', $redirect);

		if ($existingUser)
		{
			// must associate: matching user
			return $this->_getExternalRegisterFormResponse($viewName, $templateName, array(
				'associateOnly' => true,
				'existingUser' => $existingUser,
				'emailMatch' => $emailMatch,
				'redirect' => $redirect
			));
		}

		$this->_assertRegistrationActive();

		return $this->_getExternalRegisterFormResponse($viewName, $templateName, array(
			'redirect' => $redirect,
			'credentials' => $credentials,
			'showDob' => empty($credentials['user']['birthday'])
		));
	}

	/**
	 * Registers a new account (or associates with an existing one) using Google.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionGoogleRegister()
	{
		$this->_assertPostOnly();

		$session = XenForo_Application::getSession();
		$credentials = $session->get('googleCredentials');

		if (!$credentials)
		{
			return $this->responseError(new XenForo_Phrase('you_did_not_grant_permission_to_access_external_account'));
		}

		$userExternalModel = $this->_getUserExternalModel();

		$doAssoc = ($this->_input->filterSingle('associate', XenForo_Input::STRING)
			|| $this->_input->filterSingle('force_assoc', XenForo_Input::UINT)
		);

		$redirect = XenForo_Application::getSession()->get('loginRedirect');
		if (!$redirect)
		{
			$redirect = $this->getDynamicRedirect(false, false);
		}

		if ($doAssoc)
		{
			$userId = $this->_associateExternalAccount();

			$userExternalModel->updateExternalAuthAssociation(
				'google', $credentials['basic']['sub'], $userId, $credentials['extra']
			);

			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				$redirect
			);
		}

		$googleUser = $credentials['user'];

		$data = $this->_input->filter(array(
			'username'   => XenForo_Input::STRING,
			'timezone'   => XenForo_Input::STRING,
			'location'   => XenForo_Input::STRING,
			'dob_day'    => XenForo_Input::UINT,
			'dob_month'  => XenForo_Input::UINT,
			'dob_year'   => XenForo_Input::UINT,
		));
		$data['email'] = $credentials['basic']['email'];

		if (!empty($googleUser['currentLocation']))
		{
			$data['location'] = $googleUser['currentLocation'];
		}
		else if (!empty($googleUser['placesLived']) && is_array($googleUser['placesLived']))
		{
			foreach ($googleUser['placesLived'] AS $place)
			{
				if (!empty($place['primary']))
				{
					$data['location'] = $place['value'];
					break;
				}
			}
		}

		if (isset($googleUser['gender']))
		{
			switch ($googleUser['gender'])
			{
				case 'male':
				case 'female':
					$data['gender'] = $googleUser['gender'];
					break;
			}
		}

		if (!empty($googleUser['birthday']))
		{
			$birthday = $this->_validateBirthdayString($googleUser['birthday'], 'y-m-d');
			if ($birthday)
			{
				$data['dob_year'] = $birthday[0];
				$data['dob_month'] = $birthday[1];
				$data['dob_day'] = $birthday[2];
			}
		}

		$writer = $this->_setupExternalUser($data);

		if (!$this->_validateBirthdayInput($writer, $birthdayError))
		{
			$writer->error($birthdayError);
		}

		$spamModel = $this->_runSpamCheck($writer);

		$writer->advanceRegistrationUserState(false);
		$writer->save();
		$user = $writer->getMergedData();

		$spamModel->logSpamTrigger('user', $user['user_id']);

		if (!empty($googleUser['image']['url']) && $this->_canApplyAvatar())
		{
			try
			{
				// get the original size
				$url = preg_replace('/(\?|&)sz=\d+/', '', $googleUser['image']['url']);
				$request = XenForo_Helper_Http::getClient($url)->request();
				$avatarData = $request->getBody();
			}
			catch (Exception $e)
			{
				$avatarData = '';
			}
			$this->_applyAvatar($user, $avatarData);
		}

		$userExternalModel->updateExternalAuthAssociation(
			'google', $credentials['basic']['sub'], $user['user_id'], $credentials['extra']
		);

		return $this->_completeRegistration($user);
	}

	protected function _getExternalRegisterFormResponse($viewName, $templateName, array $extraParams = array())
	{
		$options = XenForo_Application::getOptions();

		$viewParams = $extraParams + array(
			'customFields' => $this->_getFieldModel()->prepareUserFields(
				$this->_getFieldModel()->getUserFields(array('registration' => true)),
				true
			),

			'timeZones' => XenForo_Helper_TimeZone::getTimeZones(),
			'tosUrl' => XenForo_Dependencies_Public::getTosUrl(),

			'dobRequired' => $options->get('registrationSetup', 'requireDob'),
		);
		return $this->responseView($viewName, $templateName, $viewParams, $this->_getRegistrationContainerParams());
	}

	protected function _setupExternalUser(array $data)
	{
		$this->_assertRegistrationActive();

		if (XenForo_Dependencies_Public::getTosUrl() && !$this->_input->filterSingle('agree', XenForo_Input::UINT))
		{
			throw $this->responseException(
				$this->responseError(new XenForo_Phrase('you_must_agree_to_terms_of_service'))
			);
		}

		$writer = XenForo_DataWriter::create('XenForo_DataWriter_User');

		$options = XenForo_Application::get('options');
		if ($options->registrationDefaults)
		{
			$writer->bulkSet($options->registrationDefaults, array('ignoreInvalidFields' => true));
		}
		$writer->bulkSet($data);

		$writer->set('user_group_id', XenForo_Model_User::$defaultRegisteredGroupId);
		$writer->set('language_id', XenForo_Visitor::getInstance()->get('language_id'));

		$customFields = $this->_input->filterSingle('custom_fields', XenForo_Input::ARRAY_SIMPLE);
		$customFieldsShown = array_keys($this->_getFieldModel()->getUserFields(array('registration' => true)));
		$writer->setCustomFields($customFields, $customFieldsShown);

		$auth = XenForo_Authentication_Abstract::create('XenForo_Authentication_NoPassword');
		$writer->set('scheme_class', $auth->getClassName());
		$writer->set('data', $auth->generate(''), 'xf_user_authenticate');

		return $writer;
	}

	protected function _validateBirthdayString($birthday, $format)
	{
		$format = strtr(preg_quote($format, '#'), array(
			'm' => '(?P<month>\d{1,2})',
			'd' => '(?P<day>\d{1,2})',
			'y' => '(?P<year>\d{1,4})',
		));
		if (!preg_match('#^' . $format . '$#i', $birthday, $match))
		{
			return false;
		}

		$month = intval($match['month']);
		$day = intval($match['day']);
		$year = intval($match['year']);

		if (!$year)
		{
			return false;
		}

		$userAge = $this->_getUserProfileModel()->calculateAge($year, $month, $day);
		$options = XenForo_Application::getOptions();
		if ($userAge < intval($options->get('registrationSetup', 'minimumAge')))
		{
			throw $this->responseException(
				$this->responseError(new XenForo_Phrase('sorry_you_too_young_to_create_an_account'))
			);
		}

		return array($year, $month, $day);
	}

	protected function _validateBirthdayInput(XenForo_DataWriter_User $writer, &$error)
	{
		$options = XenForo_Application::getOptions();
		$error = false;

		$writer->checkDob();

		if (!$options->get('registrationSetup', 'requireDob'))
		{
			return true;
		}

		// dob required
		if (!$writer->get('dob_day') || !$writer->get('dob_month') || !$writer->get('dob_year'))
		{
			$error = new XenForo_Phrase('please_enter_valid_date_of_birth');
			return false;
		}
		else
		{
			$userAge = $this->_getUserProfileModel()->getUserAge($writer->getMergedData(), true);
			if ($userAge < 1)
			{
				$error = new XenForo_Phrase('please_enter_valid_date_of_birth');
				return false;
			}
			else if ($userAge < intval($options->get('registrationSetup', 'minimumAge')))
			{
				// TODO: set a cookie to prevent re-registration attempts
				$error = new XenForo_Phrase('sorry_you_too_young_to_create_an_account');
				return false;
			}
		}

		return true;
	}

	protected function _runSpamCheck(XenForo_DataWriter_User $writer, array $extraErrors = array())
	{
		/** @var XenForo_Model_SpamPrevention $spamModel */
		$spamModel = $this->getModelFromCache('XenForo_Model_SpamPrevention');

		if ($extraErrors || $writer->getErrors())
		{
			return $spamModel;
		}

		$spamResponse = $spamModel->allowRegistration($writer->getMergedData(), $this->_request);
		switch ($spamResponse)
		{
			case XenForo_Model_SpamPrevention::RESULT_DENIED:
				$spamModel->logSpamTrigger('user', null);
				$writer->error(new XenForo_Phrase('spam_prevention_registration_rejected'), 'spam');
				break;

			case XenForo_Model_SpamPrevention::RESULT_MODERATED:
				$writer->set('user_state', 'moderated');
				break;
		}

		return $spamModel;
	}

	protected function _getExternalAuthRedirect()
	{
		$redirect = XenForo_Link::convertUriToAbsoluteUri($this->getDynamicRedirect(), true);
		$baseDomain = preg_replace(
			'#^([a-z]+://[^/]+).*$#i',
			'$1',
			XenForo_Link::convertUriToAbsoluteUri(XenForo_Application::getOptions()->boardUrl, true)
		);
		if (strpos($redirect, $baseDomain) !== 0)
		{
			$redirect = XenForo_Link::buildPublicLink('canonical:index');
		}

		return $redirect;
	}

	protected function _associateExternalAccount()
	{
		$associate = $this->_input->filter(array(
			'associate_password' => XenForo_Input::STRING
		));

		$loginModel = $this->_getLoginModel();
		$userModel = $this->_getUserModel();

		$visitor = XenForo_Visitor::getInstance();
		if (!$visitor['user_id'])
		{
			throw $this->responseException(
				$this->responseError(
					new XenForo_Phrase('to_associate_existing_account_first_log_in')
				)
			);
		}

		if ($loginModel->requireLoginCaptcha($visitor['username']))
		{
			throw $this->responseException(
				$this->responseError(
					new XenForo_Phrase('your_account_has_temporarily_been_locked_due_to_failed_login_attempts')
				)
			);
		}

		$userId = $userModel->validateAuthentication(
			$visitor['username'], $associate['associate_password'], $error
		);
		if (!$userId)
		{
			$loginModel->logLoginAttempt($visitor['username']);
			throw $this->responseException(
				$this->responseError($error)
			);
		}

		return $userId;
	}

	protected function _canApplyAvatar($combinationId = null)
	{
		/** @var XenForo_Model_Permission $permissionModel */
		$permissionModel = $this->getModelFromCache('XenForo_Model_Permission');

		if (!$combinationId)
		{
			$combinationId = $permissionModel->getPermissionCombinationIdByUserRole(
				0, array(XenForo_Model_User::$defaultRegisteredGroupId)
			);
		}
		if (!$combinationId)
		{
			return false;
		}

		$permissionSet = $permissionModel->getPermissionCombinationById($combinationId);
		if (!$permissionSet)
		{
			return false;
		}

		$permissions = $permissionSet ?
			XenForo_Permission::unserializePermissions($permissionSet['cache_value'])
			: null;

		return ($permissions
			&& XenForo_Permission::hasPermission($permissions, 'avatar', 'allowed')
			&& XenForo_Permission::hasPermission($permissions, 'avatar', 'maxFileSize') != 0
		);
	}

	protected function _applyAvatar(array $user, $data)
	{
		$success = false;
		if (!$data || !$user['user_id'])
		{
			return false;
		}

		$avatarFile = tempnam(XenForo_Helper_File::getTempDir(), 'xf');
		if ($avatarFile)
		{
			file_put_contents($avatarFile, $data);

			try
			{
				$user = array_merge($user,
					$this->getModelFromCache('XenForo_Model_Avatar')->applyAvatar($user['user_id'], $avatarFile)
				);
				$success = true;
			}
			catch (XenForo_Exception $e) {}

			@unlink($avatarFile);
		}

		return $success;
	}

	protected function _assertRegistrationActive()
	{
		if (!XenForo_Application::get('options')->get('registrationSetup', 'enabled'))
		{
			throw $this->responseException($this->responseError(
				new XenForo_Phrase('new_registrations_currently_not_being_accepted')
			));
		}
	}

	protected function _assertBoardActive($action)
	{
		switch (strtolower($action))
		{
			case 'facebook':
			case 'twitter':
			case 'google':
				break;

			default:
				parent::_assertBoardActive($action);
		}
	}

	protected function _assertCorrectVersion($action)
	{
		switch (strtolower($action))
		{
			case 'facebook':
			case 'twitter':
			case 'google':
				break;

			default:
				parent::_assertCorrectVersion($action);
		}
	}

	protected function _assertViewingPermissions($action) {}

	protected function _assertTfaRequirement($action) {}

	/**
	 * Session activity details.
	 * @see XenForo_Controller::getSessionActivityDetailsForList()
	 */
	public static function getSessionActivityDetailsForList(array $activities)
	{
		return new XenForo_Phrase('registering');
	}

	/**
	 * @return XenForo_Model_User
	 */
	protected function _getUserModel()
	{
		return $this->getModelFromCache('XenForo_Model_User');
	}

	/**
	 * @return XenForo_Model_UserProfile
	 */
	protected function _getUserProfileModel()
	{
		return $this->getModelFromCache('XenForo_Model_UserProfile');
	}

	/**
	 * @return XenForo_Model_UserField
	 */
	protected function _getFieldModel()
	{
		return $this->getModelFromCache('XenForo_Model_UserField');
	}

	/**
	 * @return XenForo_Model_UserConfirmation
	 */
	protected function _getUserConfirmationModel()
	{
		return $this->getModelFromCache('XenForo_Model_UserConfirmation');
	}

	/**
	 * @return XenForo_Model_UserExternal
	 */
	protected function _getUserExternalModel()
	{
		return $this->getModelFromCache('XenForo_Model_UserExternal');
	}

	/**
	 * @return XenForo_Model_Login
	 */
	protected function _getLoginModel()
	{
		return $this->getModelFromCache('XenForo_Model_Login');
	}
}

 

38 minutes ago, 3aGl3 said:

If you could give me more of the PHP I can probably tell you.
Is there something like a register function of some sort?

Doh...

Here is a randomly created user.

That blob is a bin file.

 

mdICngt.png

 

 

https://xenforo.com/community/threads/xf-db-tables.18825/

 

It contains: a:1:{s:4:"hash";s:60:"$2a$10$jlWdNFyeOgxR.ogqhyVmwOyAFIlZWWWj3g6Z8MlUChvYVFHbqF8Ei";}

Link to comment
14 minutes ago, 3aGl3 said:

Hm, sorry I can't make much of that.
It gets a writer class of some sort...that then writes to the database.

https://github.com/Contex/XenAPI/wiki/REST-API-Actions

 

This seems to work.

Request: api.php?action=authenticate&username=USERNAME&password=PASSWORD
Example: api.php?action=authenticate&username=Contex&password=MyPassword
Response:

{
  "hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}

 

You think you can figure the way it generates the hash? It gives nothing back if it fails.

 

 

{"error":5,"message":"Authentication error: Invalid username or password!"}

 

https://github.com/Contex/XenAPI/blob/master/net/xenapi/XenAPI/api.php

As far as I can tell It somehow deserializes the data line from the record?

No idea how would that look in lua lmao.

 

Guess i could do webrequests and use this API at login?

Edited by Dretax
Link to comment
Just now, 3aGl3 said:

I guess you could do that.
The hash returned is for any further API requests from that user and thus probably irrelevant for you.

Possibly. Any idea how would I reproduce the same thing, if i could in lua?

 

Or how would i do the webrequests for that? Would those be fast enough at all?

Link to comment
  • 11 months later...
On 2017. 04. 17. at 12:22, 3aGl3 said:

I guess you could do that.
The hash returned is for any further API requests from that user and thus probably irrelevant for you.

I know that the thread is old but here is the solution.

I have no idea why the sh*tty MTA API doesn't support 2a bcrypts since Its not a big deal, but here is the trick.

https://github.com/mabako/mta_bcrypt

 

local PasswordQuery = dbQuery( MYSQL, "SELECT `data`, `user_id` FROM `xf_user_authenticate` WHERE `user_id` = (SELECT `user_id` FROM `xf_user` WHERE `username` = '??') LIMIT 1", user)
local PasswordQueryResult, lines1 = dbPoll ( PasswordQuery, -1 )
if lines1 ~= 1 then
	return
end
local userwebid = tonumber(PasswordQueryResult[1]["user_id"])
local passwordblob = PasswordQueryResult[1]["data"]
local passwordblobreplace = string.gsub(passwordblob, '"', "", 2)
local bcrypthash = string.match(passwordblobreplace , '"([^"]*)"')

local match = bcrypt_checkpw(password, bcrypthash)

 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...