I add a user to the database.
I encode their password using bcrypt encryption:
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user->getPassword(),$user->getSalt());
$postData->setPassword($password);
This all persists to the database just fine.
On my User Entity my getSalt() method returns null:
public function getSalt(){
return null;
}
Now when I try to authenticate the symfony logs tells me that I am entering bad credentials.
However...
If i use this site to encrypt my password with bcrypt:
http://bcrypthashgenerator.apphb.com/
Then enter the result into the database manually and try and authenticate with the users password i have updated with the results from the above link,
It works fine.
So there is something I have not set, that when I am encrypting the password that symfony cannot authenticate against it.
I have tried the setting a salt on the getSalt(), setting it to an empty string.
EDIT
I am using php 5.5.9
I have also tried using the BCryptPasswordEncoder($cost) class,
setting the cost on the __construct the same as what's in my security.yml
and it still does not authenticate.
EDIT
I have also tried installing this class via composer to as suggested here.
EDIT
If I use the php method:
password_hash('testertester',PASSWORD_BCRYPT,array('cost'=>13));
It authenticates perfectly.
So its something to do deeper in Symfony. I'll keep digging.
Still no luck :-/
I am scouring the web to no avail so far.
Any help would be greatly appreciated.
Ad
I was passing in the existing entity object not the submitted data object!!
$postData = $form->getdata();
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user->getPassword(),$user->getSalt());
$postData->setPassword($password);
Note this line here:
$user->getPassword() needed to be $postData->getPassword()
$password = $encoder->encodePassword($postData->getPassword(),$user->getSalt());
You live and learn ay!
Related
I've been spending a few days troubleshooting a failure of certain passwords to validate in Laravel 9. The password testperson resolves to the hash $2y$10$5xc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW. A direct query on the corresponding database table confirms that this is the correct hash. Yet Laravel's authentication infrastructure rejects this password and denies authentication.
This is not universal. I have multiple passwords that are resolving correctly. For example, the password eo resolves to $2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2, and Laravel authenticates that password. The same mechanism creates both of these user records, though they have different permissions (indicated by boolean values on the record).
I tracked down the bug to the function password_verify, which was identified as returning false negatives in this Stack Overflow question and this Treehouse thread.
Specifically, here is the stack in Laravel that gets down to this failure point:
The login route calls \Illuminate\Foundation\Auth\AuthenticatesUsers::login via the controller class.
The login method calls \Illuminate\Foundation\Auth\AuthenticatesUsers::attemptLogin.
The attemptLogin method calls the attempt method of the controller's guard object.
\Illuminate\Auth\SessionGuard::attempt calls \Illuminate\Auth\SessionGuard::hasValidCredentials.
\Illuminate\Auth\SessionGuard::hasValidCredentials calls the validateCredentials method on the guard's provider object.
Illuminate\Auth\EloquentUserProvider::validateCredentials calls the check method on its hasher object.
Illuminate\Hashing\HashManager::check calls the check method on its driver.
Illuminate\Hashing\BcryptHasher::check calls Illuminate\Hashing\AbstractHasher::check.
Illuminate\Hashing\AbstractHasher::check calls password_verify.
After unwinding this entire stack, I ran the following code in the login method of the login controller:
$provider = $this->guard()->getProvider();
$credentials = $this->credentials($request);
$user = $provider->retrieveByCredentials($credentials);
$password_unhashed = $request['password'];
$password_hashed = $user->getAuthPassword();
$password_verify = password_verify($password_unhashed, $password_hashed);
logger('attemping login', compact('password_verify','password_unhashed','password_hashed'));
That dumps this context:
{
"password_verify": false,
"password_unhashed": "testperson",
"password_hashed": "$2y$10$5xc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW"
}
And if I put that password into a SELECT users WHERE password= query, I get the user that I'm expecting.
What's going on here? And how do I get around this?
I think your assertion that the hash you provided is a hash of 'testperson' is in fact false. Since hashing is one-way, I can't tell you what the hash you showed is derived from. NOTE: This runs on PHP 7.4, but I don't think it will work on PHP 8 and beyond because of the deprecation of the salt option in password_hash().
<?php
//$testhash = '$2y$10$5xc/wAmNCKV.YhpWOfyNoetCj/r3Fs5TyAskgZuIF/LEItWfm7rPW';
$testhash = '$2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2';
//$password = "testperson";
$password = "eo";
$options = array("cost" => 10, "salt" => substr($testhash, 7, 22));
$pwhash = password_hash($password, PASSWORD_BCRYPT, $options);
echo $pwhash."\n";
$salt = substr($pwhash, 0, 29);
echo $salt."\n";
$cryptpw = crypt($password, $salt);
echo $cryptpw."\n";
if (password_verify($password, $cryptpw)) {
echo("Verified.\n");
} else {
echo("NOT Verified.\n");
}
if (password_needs_rehash($cryptpw, PASSWORD_BCRYPT, $options)) {
echo("Needs rehash.\n");
} else {
echo("Doesn't need rehash.\n");
}
/*
testperson results...
$2y$10$5xc/wAmNCKV.YhpWOfyNoeVNPMEcYrxepQeFAssFoAaIYs4WLmgZO
$2y$10$5xc/wAmNCKV.YhpWOfyNoe
$2y$10$5xc/wAmNCKV.YhpWOfyNoeVNPMEcYrxepQeFAssFoAaIYs4WLmgZO
Verified.
Doesn't need rehash.
eo results...
$2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2
$2y$10$uNWYvMVmagIwQ2eXnVKLCO
$2y$10$uNWYvMVmagIwQ2eXnVKLCOAK1QFQdcRtxbvlghf.Xpg0U1w.N./N2
Verified.
Doesn't need rehash.
*/
?>
I have a call to Hash::make in the observer for the user class. I discovered that it was running even though it wasn't supposed to, resulting in a duplicate hash.
I have a need to manually encrypt the password in the same way Symfony does it.
$user->setPlainPassword($password);
$userManager->updateUser($user);
This is application code that saves the user password. But it is obviously encrypted (not plain as method says).
How can I manually get the same result for given password?
EDIT1:
The problem is that I want to authenticate user manually in users controller code. I have readable username and password that comes as parameters. I want to return 401 if they to not exist in database.
Symfony comes with a useful command to encode a password the same way it does:
bin/console security:encode-password 'your_plain_password' 'AppBundle\Entity\YourUserClass'
Try this in your controller action:
$encoder = $this->get('security.encoder_factory')->getEncoder($userClass);
$encodedPassword = $encoder->encodePassword($plainPassword);
In newer Symfony versions (5.3+) you can do it by running CLI command:
bin/console security:hash-password
I'm trying to integrate BorisMorel LdapBundle with FOSUser following this guide: Using a custom user manager with LDAP authentication
My objective is to authenticate by using LDAP and then storing the LDAP data in my fosuser table.
I've got to the point where the data is correctly retrieved from the LDAP and stored in the fosuser table. I don't want it to store the password on the database, because there's no use on it, as I prefer the password being validated using the LDAP on each login.
The problem is when I get to this point:
C:\xampp\htdocs\Symfony2\vendor\symfony\symfony\src\Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider.php
In function:
public function authenticate(TokenInterface $token)
It calls the function:
$this->checkAuthentication($user, $token);
Which resolves this function:
checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
Which is in this file:
C:\xampp\htdocs\Symfony2\vendor\symfony\symfony\src\Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider.php
Because it checks the password using this:
encoderFactory->getEncoder($user)->isPasswordValid($user->getPassword(), $presentedPassword, $user->getSalt())) {
throw new BadCredentialsException('The presented password is
}
As the password on the fosuser table is empty, the authentication always fails, even if it already passed the LdapBundle test.
What I'm missing? I'm guessing that the wrong validator is being used, but it's not described in the BorisMorel resources!.
Please help!
EDIT Originally I thought Oauth2 is the way to go but maybe it is not. I'll leave that out of this question for now as it is confusing things.
I'm creating a mobile app (Android/iOS). I'd like the user to enter their credentials (user/pass) in the mobile device which would then get sent to my server (Joomla CMS) to verify the credentials and create/send a token. I don't want to store the user/pass on the device just the token.
In addition this token needs to have a timeout to be refreshed when needed. Such as credentials have changed.
At this point I'm trying to figure out what the architecture of this will look like.
Are there any tutorials on how you can achieve this (ideally with Joomla)? Anything that someone could point me to?
You should post the username and password from the mobile app and from there on you should follow the solution provided in this question:
https://stackoverflow.com/a/2188969/900617
The end solution is to create my own Joomla component. Pretty much everything is in my controller. Not the final code but something like this will work.
defined('_JEXEC') or die;
jimport('joomla.application.component.controller');
class FooauthController extends JController
{
function __construct() {
// params
$jinput = JFactory::getApplication()->input;
$this->username = $jinput->get('user', '', 'STRING');
$this->password = $jinput->get('password', '', 'STRING');
$this->checkParameters();
}
private function checkParameters() {
// datatype checks
if ($this->username == '' || $this->password == '') {
header('HTTP/1.1 400 Bad Request', true, 400);
}
}
private function createToken() {
// token generation - what Joomla does (just an example)
jimport('joomla.user.helper');
$salt = JUserHelper::genRandomPassword(32);
$crypted = JUserHelper::getCryptedPassword($password, $salt);
$cpassword = $crypted.':'.$salt;
return $cpassword;
}
function execute() {
// Get the global JAuthentication object
jimport( 'joomla.user.authentication');
$auth = & JAuthentication::getInstance();
$credentials = array( 'username' => $this->username, 'password' => $this->password );
$options = array();
$response = $auth->authenticate($credentials, $options);
// success
if ($response->status === JAUTHENTICATE_STATUS_SUCCESS) {
$response->status = true;
echo json_encode($this->createToken());
} else {
// failed
$response->status = false;
echo json_encode($response);
}
}
}
This represents a component called com_fooauth. Now the native app will send a query like this:
http://www.myhost.com/index.php?option=com_fooauth&user=username&password=pass&format=raw
Kind of a short cut to put everything in the controller, but hopefully you get the idea.
I hope that I understand correctly your use case.
If you want to use oAuth, then your mobile apps are considered as the oAuth-client.
Your "server" holds the "protected resources", and it can be used only with oAuth access-token, so it is called "resource server". Now you want something to supply this access-token, so this is the identity-provider, AKA authentication server, e.g. Facebook, Google, (or implement one by your own).
The flow is (generally): the user (mobile app) tries to reach a protected resource; since it has no token, he is being redirected to the auth-server. the latter is responsible for the user/password login page, and creating the token.
If it is true - you still can implement everything by your own, without using Facebook/Google APIs, because oAuth has SPECs. However, it can be easier for you to use the providers' packages.
EDIT: reconsider the usage of oAuth
You use oAuth only if you want your webapp to support oAuth SPEC. There are several benefits, one of them is that you can use 3rd party identity provider, e.g. Yahoo! and use their identities without managing them. So if I have a user in Yahoo!, I can use your app without additional registrations (your app will have to support access-tokens from Yahoo!). But in your case, you are about to implement all the logic of identity-provider (forgot password, change password, registration, etc) plus supporting oAuth - and all of this without enjoying the benefits of oAuth at all! So - you have to reconsider the usage of oAuth...
You need to use their APIs as a base. They aren't going to just let you build your own API that connects to their database, that to them would look more like a password cracker than an API.
This isn't Joomla or a tutorial, (and I'm very rusty in php) that said...
First a few caveats:
* memcache isn't secure & this implementation has you putting username / password in: Be sure that it is safely behind a firewall, or else encrypt it first. Happy to give some pointers on that if you need it.
* memcache isn't guaranteed not to drop data if it runs out of memory. In practice it is reliable, but your app should handle that gracefully. If you don't want to lose data like that, just substitute something like couchbase for memcache.
* just returning a token in response to a login probably isn't super useful. I'd json-ize the token along with stuff like the user name, and any other info to get the app up and running without needing to make a second API call.
* the code below doesn't handle error cases, I can call them all out in more detail if that isn't obvious to you.
If it were me, I'd just use memcache to persist the tokens & map that token to the username & password that was originally passed. You can use the memcache time to live to get your expiration for free.
Send username / password to the server (ideally over https).
Create a random string or guid (eg: http://php.net/manual/en/function.uniqid.php or http://www.lateralcode.com/creating-a-random-string-with-php/) , this is your token
Store the username / password in memcache with that token as a key
Set a timeout
$token = createToken("user1234", "pass2324");
print "Token: $token \n\n";
$credentials = credtialsFromToken($token);
print "Credentials from the token: ";
var_dump($credentials);
print "\n\n";
function setup() {
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ("Could not connect");
}
function createToken($user, $pass) {
$TOKEN_EXPIRE_TIME=60 * 60 * 24 * 30;
$credentials = array(
"user" => $user,
"pass" => $pass,
);
$token = uniqid( );
memcache_set($token, credentials, 'some variable', 0, 30);
return $token;
}
function credtialsFromToken($token) {
$credentials = memcache_get($token);
return $credentials;
}
If the token is incorrect or expired, they get an null credentials back and have to login.
Edit: cleaned it up into functions that appear to work in php...
So I am trying to create a new Silex application and use the Security bundle included. For simplicities sake I was going to go with the basic password encoding.
Per the Silex documentation http://silex.sensiolabs.org/doc/providers/security.html I have created a custom User Provider. However this user interface does not seem to use the default password encoding.
I can successfully get a password out of
$password = $app['security.encoder.digest']->encodePassword('foo');
However when I use the example
// find the encoder for a UserInterface instance
$encoder = $app['security.encoder_factory']->getEncoder($user);
// compute the encoded password for foo
$password = $encoder->encodePassword('foo', $user->getSalt());
I get the
RuntimeException: No encoder has been configured for account
In symfony2, I would use something like the following
encoders:
somename:
class: Acme\DemoBundle\Entity\User
Acme\DemoBundle\Entity\User: sha512
Acme\DemoBundle\Entity\User: plaintext
Acme\DemoBundle\Entity\User:
algorithm: sha512
encode_as_base64: true
iterations: 5000
Acme\DemoBundle\Entity\User:
id: my.custom.encoder.service.id
But that doesnt seem to be the case here. I can't seem to find any type of setEncoder method so I am a bit stumped.
You need to reconstruct the EncoderFactory to add your custom implementation:
<?php
$app = new Silex\Application();
$app['myapp.encoder.base64'] = new Base64PasswordEncoder();
$app['security.encoder_factory'] = $app->share(function ($app) {
return new EncoderFactory(
array(
'Symfony\Component\Security\Core\User\UserInterface' => $app['security.encoder.digest'],
'MyApp\Model\UserInterface' => $app['myapp.encoder.base64'],
)
);
});
(oh and please, don't use a Base64Encoder() for password ;))
I was able to use the accepted answer to fix my problem, but I couldn't assign it to security.encoder_factory directly, so I'm just sharing what I found to work.
instead of:
$app['security.encoder_factory'] = $app->share(function($app) {
//..see above...//
});
I had to use:
$app->register(new Silex\Provider\SecurityServiceProvider(),array(
'security.encoder_factory' => $app->share(function($app) {
//... same as above ...//
})
));
I'm too new to Silex to know why it didn't work as above for me. My initial guess would be a version difference (question was asked over two years ago). I can assign to security.provider.default before the call to register the module, but I can't seem to assign to security.encoder_factory . I also seem to have to put security.firewalls in the register call.