Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

J_shelfwood's avatar

Using bcrypt() in testing to compare passwords

I've made an endpoint in my application dedicated to changing a user's password. It works like expected but when I try to make my test work I run into an issue.

This is my test:

    /** @test*/
    public function an_user_can_change_its_password()
    {
        $data = [
            "password" => "newpassword",
            "password_confirmation" => "newpassword"
        ];

        $this->put("/api/users/{$this->user->id}/password", $data)
            ->assertStatus(200);

        $this->assertEquals(User::find($this->user->id)->password, bcrypt("newpassword"));
    }

When I try to run it it fails, it tries to compare 2 hashes that are different... Quite unexpected behaviour. Now I'm thinking that they both use different keys for hashing the strings. That leaves me wondering, how do I test my endpoint?

0 likes
7 replies
bobbybouwmann's avatar
Level 88

Well you can't compare two hashed values because the hashed output will always be different.

So small example: Let's say I have string a and string b and when I bcrypt both I get the following results

$stringA = 'test';
$stringB = 'test';

$hashA = bcrypt($stringA); // y$swSPpBL.kOMGRltYIWcc1.6f2u0icyRFT8DCgchzDpTv3Bz7r5wGm

$hashB = bcrypt($stringB); // y$vD3DArbNt52OCLnXGzOxlOGqQ8VICBDBjcZwFJG5IoIu5TlVKzOsC

$this->assertEquals($hashA, $hashB); // returns false

Now this will fail of course! So there is no way to bcrypt the same value twice and get the same output. This is because the salt (app_key) is used to generate a unique string that hashes the value.

So if you want to compare the two you need to use a different method. So instead of checking if the string is equal to the hash you should check if the given hash returns the correct password. I believe you can do something like this

$hashA = bcrypt($stringA); // y$swSPpBL.kOMGRltYIWcc1.6f2u0icyRFT8DCgchzDpTv3Bz7r5wGm

if (Hash::check('test', $hashA)) {
    return true;
}

So in your test you can do this

$user = User::find($this->user->id);

$this->assertTrue(Hash::check('newpassword', $user->password));
4 likes
caldicot's avatar

I came across the same question, but there is still one point I do not understand. I understood the answer @bobbybouwmann gave: I cannot compare two hashes, as they are different when salted.

Some background: I am developing a small REST API with Lumen and a Electron/JS client. I do have a LoginController that should authenticate the user and return a JWT token when successfully logged in.

In this step I have to transmit the password from the client application to the server. To compare the hashed password in the database with the password the user has entered in the client application, I've to transfer the password in plain text. Is that really the way to go? I'll always need the plain password on the server for comparison?

One improvement would be to use an async encryption to avoid sending a plain password. The client can only encrypt, while the server can only decrypt the password.

Could one please describe the step how the client should send the password to the server in more detail?

djug's avatar

here is a different approach that might help you:

if you think about it what we really want to test is the following:

  • if the user still could login with the new password

so a test like the following one should suffice:

$authenticated = \Auth::attempt(["email" => $this->user->email , "password" => $data['password']);

$this->assertTrue($authenticated);
caldicot's avatar

@djug thanks for your reply. Maybe my question was offtopic, as the the original question aimed at the testing procedure.

My question is, how to transfer the password from a client to the REST API, that response with an JWT token, when login is possible. As @bobbybouwmann said, I cannot compare two hashes. So should I transfer plain text password? Should I encrypt the password on the client and decrypt on the server side and then check the password against hash?

bobbybouwmann's avatar

@CALDICOT - Yeah, never send plain passwords over the internet! Use some hashing that can be done on both client and server. This way it's already more secure! You can even hash it with the JWT token if you like.

caldicot's avatar

@bobbybouwmann thanks for your answer. Unfortunately, it's still not completely clear to me.

I understand, that I should not send plain text passwords over the internet. But how can I avoid that in detail? When hashing the password with bcrypt on the client side. The hash is salted and changes each time. In addition, as far as I understood, hashing is an irreversible operation. When sending a hashed (not salted) password to the server. How can the server check if the password is correct? The servers only saves the hashed pw in the database - in this case even salted. From my point of view, the same is true for JWT tokens. I would need the same secret for generation and extraction on client and server side. This seems not very secure to me, as everyone extracting the secret from the application could login, right?

Could you please explain the procedure in more detail?

Please or to participate in this conversation.