tymondesigns / jwt-auth

🔐 JSON Web Token Authentication for Laravel & Lumen
https://jwt-auth.com
MIT License
11.28k stars 1.55k forks source link

Token provides wrong user #1487

Open aldyson opened 6 years ago

aldyson commented 6 years ago

I created 2 accounts with different email addresses and passwords. I have a HTTP request that sends the token (stored in local storage after signin) like this:

http://apiurl/api/projects/getProjects?token=tokenString

Inside the method, I used the following line to retrieve the authenticated user based on the token:

$authenticatedUser = JWTAuth::parseToken()->authenticate();

This retrieves the email address for the first account I created, even if I sign in on the second account. For each account the token in the request is different but the authenticatedUser is for the same account every time.

This only ever happens for one specific account, where it thinks I am logged in as a different user. If I remove all my users from the database and create some new accounts, then one of them will have this same issue.

darrynten commented 6 years ago

I've seen this problem intermittently but have not yet figured it out.

@aldyson do you have any extra info on this?

aldyson commented 6 years ago

@darrynten I have noticed that it only ever happens with one account - i.e. if I create 10 accounts, just one of them will retrieve the incorrect user id, emai land all other details from the token.

Clearing the database and re-creating the accounts doesn't resolve the issue, as it re-occurs on another random account.

It could be version related, as I believe i'm running an older version of the package, but i'm not sure about this.

aldyson commented 6 years ago

@darrynten The version shown in my composer.json file is "tymon/jwt-auth": "0.5.*",

aldyson commented 6 years ago

@darrynten Do you know anything else about this issue? It's a really big blocker for me at the minute.

ntopulos commented 6 years ago

I have the same problem, I was testing my API and some tests were failing. At the end these issues seemed to originate in a problem of jwt-auth when it comes to distinguish users.

I wrote a test to verify this assumption and jwt-auth seems indeed to return the wrong user for the provided credentials.

My test is doing something like this:

// 1. Create a db user
$dbUser;

// 2. Get a JWT token
$jwtToken = JWTAuth::attempt([
    'email' => $user->email,
    'password' => 'secret'
]);

// 3. Call the API that returns the user uuid based on the authorisation bearer
$apiUuid = JWTAuth::toUser()->uuid;

// 4. Compare uuid from db with the one returned by the API
$this->assertEquals($dbUser->uuid, $apiUuid);

I run this test multiple times in a loop. On the first iteration, it works, both uuids are equal, but on the second iteration the uuid returned by jwt-auth is the uuid of the first iteration.
The issue is independent of the number of users in the database: the first iteration works on an empty user table as well as on a populated one.

The tests seem ok, I have no idea how jwt-auth could make such an error. Is there some kind of cache that could be involved?

tcc-world commented 6 years ago

I had a serious problem with JWT. Following sctoch.io's tutorial for using mongodb with passport.js, I found that out of around 3000 users that I had, some of them were logging in with wrong user info and they could see other user's dashboards. I tried every thing, no clue, then I found one more victime like me. 1-2 times in thousand, people would open wrong user's Id. Finally I decided to remove this json web token thing altogether, and the problem was gone. :( strange that no info is there on the whole internet regarding this weird problem.

ntopulos commented 5 years ago

I found a solution to this problem:

  1. If you are on version 0.5.*, update to 1.0.0-rc.3.
  2. Replace JWTAuth::toUser() by auth()->user().

I did not try to find out why, but the jwt-auth returns the wrong user when using JWTAuth::toUser(), at least during tests.

aldyson commented 5 years ago

@ntopulos Thank you! I'll try this out and see if it solve the issue for me too. I was hoping that updating the version may solve it so I'll give it a go :)

ntopulos commented 5 years ago

@aldyson Sorry, but I noticed afterwards that this update caused the authentication to fail elsewhere. Then trying to fix one bug, created a another one and so on. If you give it a try, let us know if you are luckier.

Official documentation for the development version

Anyways, at the end Passport seems to be a wise alternative.

aldyson commented 5 years ago

@ntopulos No problem, I'll let you know how it goes.

aldyson commented 5 years ago

A quick update on this issue:

I followed steps suggests by @ntopulos and for now (at least) the issue seems to have been resolved. I will test this over the next few days / weeks as the issue wasn't very common in the first place.

For me at least, what seemed to solve this was the following approach -

I'll update this issue with any potential further issues I find, or if I find that these changes have resolved the issue.

aldyson commented 5 years ago

An update on this - The above changes didn't solve the issue and I've given up trying to resolve it. Since it's too much of a blocker I'll be using Firebase which is probably a much better solution based on my own requirements anyway. Hopefully someone can get to the bottom of this though.

Devamit2016 commented 4 years ago

still have this issue?if anyone has overcome this problem please provide solution

kadekjayak commented 4 years ago

just have this issue on prod app.. i switch to passport immediately

fancytboy commented 4 years ago

Had the same issue with mine too. Apparently looking at the payload using JWTAuth::payload(), the "sub" claim was null. The sub claim is the value which contains the user id that matches the user primary key in the database. Since it was null, Laravel picked the first value that existed in the Database. First you need to fix this by adding

public function getKey(){ return $this->ID; }

In your user Model file.

Also make sure you set getJWTIdentifier like this

`/**

see Documentation

Now if you check the payload using JWTAuth::payload() it should contain a value for "sub" which will be the primary key for the authenticated user.

But mostly likely you'll get an error like this *Column not found: 1054 Unknown column '' in 'where clause' (SQL: select from ...** when you try to access a protected route

This is because in Illuminate\Auth\EloquentUserProvider->retrieveById, Laravel tries to get the getAuthIdentifierName from the model but it doesn't exist. so you need to set the getAuthIdentifierName to the name of the primary Key field in your user Model file like this

public function getAuthIdentifierName() { return 'ID'; }

Or you could set it in the config/jwt.php file and get if from the provider/guard whichever suits your coding style. Personally i prefer the simple version above

Hope this helps

stale[bot] commented 3 years ago

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

aidancrane commented 3 years ago

If I do the following,

$user = User::where("email", $email_or_username)->where("email", "!=", "")
                     ->orWhere('username', $email_or_username)->first();
if ($user != null) {
    if (Hash::check($password, $user->password)) {
            $token = auth('api')->login($user);
            return $this->respondWithToken($token);
    }
}

$token seems to be a token for the first user example if you put a jwt token in there from this function it returns a sub of 1 every time.

I managed to fix it by doing the following, but it still does not work on the other side,

protected function respondWithToken($token)
    {
        // JWT Auth provides wrong token, this fixes that problem.
        $token = auth('api')->tokenById(auth('api')->user()->id);
        // JWT sub is wrong id on send, this fixes that.
        return response()->json([
            'status' => 'Success',
            'message' => 'Authentication was successful, welcome back ' . auth('api')->user()->name . '.',
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60,
            'user_name' => auth('api')->user()->username
        ]);
    }

If in a middleware I do the following after,

                if (auth('api')->user()) {
                    $payload = auth('api')->payload();
                    dd($payload->toArray());
                }

the payload sub is 1 again :(

aidancrane commented 3 years ago

The following fixed the sub being 1 again, but obviously this is not an ideal fix as the header is not validated properly.

if (auth('api')->user()) {
    $sub = json_decode(base64_decode(explode(".", $request->header('Authorization'))[1]))->sub;
    auth('api')->login(User::where("id", $sub)->first());
}
noecs commented 3 years ago

I encountered a similar error. I use auth()->user(), and the user serial number appears intermittently. There are about 100,000 users in my online project. After logging in to the generated jwt base and decrypting it, I found that the user id of sub is correct. However, the users obtained after parsing by the guards are randomly changed.