As of writing this post, hashing passwords with Argon2 algorithm is recommended by IETF. In this example we are going to use Sodium to hash and verify passwords with Argon2id in our PHP 7.2 application.


Class


You might need to add "ext-sodium": "*" to your composer.json file.


class PasswordHasher
{
public function hash(string $plainPassword): string
{
return sodium_crypto_pwhash_str(
$plainPassword,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);
}

public function verify(string $plainPassword, string $hashedPassword): bool
{
$result = sodium_crypto_pwhash_str_verify($hashedPassword, $plainPassword);

sodium_memzero($plainPassword);

return $result;
}
}

Test


class PasswordHasherTest extends TestCase
{
public function testHash(): void
{
$hash = (new PasswordHasher())->hash('inanzzzinanzzz');

$this->assertStringStartsWith('$argon2id$v=19$m=65536,t=2,p=', $hash);
$this->assertSame(97, \strlen($hash));
}

/**
* @dataProvider passwordDataProvider
*/
public function testVerify(string $plainPassword, string $hashedPassword, bool $result): void
{
$this->assertSame($result, (new PasswordHasher())->verify($plainPassword, $hashedPassword));
}

public function passwordDataProvider(): array
{
return [
'Test with invalid password but valid hash' => [
'$plainPassword' => 'invalid_password',
'$hashedPassword' => '$argon2id$v=19$m=65536,t=2,p=1$Lum8Qyd9RS+q5wlDYHfACA$1YUvs5HNdq7MVY0YAVV7SxyrSp309CfyS3BUFfuR+GE',
'$result' => false,
],
'Test with valid password but invalid hash' => [
'$password' => 'inanzzzinanzzz',
'$hashedPassword' => 'invalid_hash',
'$result' => false,
],
'Test with valid password and hash' => [
'$password' => 'inanzzzinanzzz',
'$hashedPassword' => '$argon2id$v=19$m=65536,t=2,p=1$Lum8Qyd9RS+q5wlDYHfACA$1YUvs5HNdq7MVY0YAVV7SxyrSp309CfyS3BUFfuR+GE',
'$result' => true,
],
];
}
}