jwt-auth icon indicating copy to clipboard operation
jwt-auth copied to clipboard

Token provides wrong user

Open aldyson opened this issue 7 years ago • 19 comments

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.

aldyson avatar Feb 19 '18 13:02 aldyson

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

@aldyson do you have any extra info on this?

darrynten avatar Mar 12 '18 11:03 darrynten

@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 avatar Mar 12 '18 11:03 aldyson

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

aldyson avatar Mar 12 '18 12:03 aldyson

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

aldyson avatar Apr 29 '18 11:04 aldyson

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?

ntopulos avatar May 04 '18 10:05 ntopulos

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.

tcc-world avatar Jun 15 '18 06:06 tcc-world

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.

ntopulos avatar Nov 07 '18 17:11 ntopulos

@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 :)

aldyson avatar Nov 08 '18 12:11 aldyson

@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.

ntopulos avatar Nov 08 '18 13:11 ntopulos

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

aldyson avatar Nov 08 '18 14:11 aldyson

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 -

  • Updating tymon/jwt-auth from "0.5.*" to "0.5.12"
  • Following the documentation here https://jwt-auth.readthedocs.io/en/develop/laravel-installation/. (Including generating a new secret key).
  • In /config/jwt.php I made the following changes: - 'jwt' => 'Tymon\JWTAuth\Providers\JWT\NamshiAdapter', to 'jwt' => 'Tymon\JWTAuth\Providers\JWT\Namshi', - 'auth' => 'Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter', to 'auth' => 'Tymon\JWTAuth\Providers\Auth\Illuminate', - 'storage' => 'Tymon\JWTAuth\Providers\Storage\IlluminateCacheAdapter', to 'storage' => 'Tymon\JWTAuth\Providers\Storage\Illuminate',
  • Changed the AuthGuards in /config/auth.php to the following: 'guards' => [ 'web' => [ 'driver' => 'api', 'provider' => 'users', ], 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],
  • Replaced JWTAuth::parseToken()->authenticate() with auth()->user()
  • Applied the auth:api middleware on all routes requiring authentication.

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

aldyson avatar Nov 10 '18 10:11 aldyson

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.

aldyson avatar Dec 05 '18 11:12 aldyson

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

Devamit2016 avatar Apr 22 '20 16:04 Devamit2016

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

kadekjayak avatar Jun 22 '20 03:06 kadekjayak

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

/** * Get the identifier that will be stored in the subject claim of the JWT. * * @return mixed */ public function getJWTIdentifier() { return $this->getKey(); }

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

fancytboy avatar Sep 11 '20 00:09 fancytboy

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.

stale[bot] avatar Dec 25 '20 15:12 stale[bot]

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 avatar May 26 '21 10:05 aidancrane

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());
}

aidancrane avatar May 26 '21 11:05 aidancrane

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.

noecs avatar Sep 17 '21 06:09 noecs