Open zerox1212 opened 6 years ago
@zerox1212 , I tried to reproduce this issue but failed. Can you show me steps needed to reproduce this? Also where did you see that JWTAuth::invalidate()
is called directly?
The below function can be called by setPasswordAttribute($password)
and it won't update the map as far as I can tell. This could be why some users report Password Reset only works the first time.
It removes the token mapping for the user at the last line of the method - invalidateTokenByUserId
return DB::table('token_map')->where('user_id', $userId)->delete();
Also I can reset a user password multiple times without running into any issue. Which version of DreamFactory you are running?
For your test you should do this with forever sessions enabled.
This error only happens to people who are using PUT to refresh tokens (in my case forever tokens). I have version 2.3.0.
I tried several ways to reproduce this but everything seems to be working correctly at my end. One thing I would double check in your case is that after your refresh your token, make sure to use the new token to reset password. In fact, for resetting password you actually don't even have to provide any token. The password reset api (api/v2/user/password) is not protected.
I will do more research into this problem and get back to you. Can you confirm that doing GET to user/session will return a new token? We followed what was posted online about doing GET instead of PUT, but I don't see how DF would send a new token using GET.
So I did more testing with this issue and found out what to is needed to remove the blacklisted
error when resetting a password. You must Flush System-wide Cache
. Then you can reset a user password without getting a token error. Any ideas where to look based on that info?
I think the real problem might be that even though the token is removed from the map, the old token is still in the cache. @df-arif
@zerox1212 , What you are seeing with flushing system-wide cache is an expected behavior. A token is blacklisted by adding it to a list of blacklisted token in cache. When you clear system-wide cache (this is typically done by system admins) it clears the blacklisted tokens in cache and therefore you don't get the error again. Eventually when this token is expired it can never be reused or be refreshed regardless of whether it is blacklisted or not.
But I believe your issue is not related to this blacklisted token cache. You should not get the blacklisted token error in the first place when you are resetting password. When you reset the password just make sure not to provide any token (JWT) as part of that API call. As I mentioned earlier the API (api/v2/user/password) for resetting password is open and doesn't require a token. I am thinking that somehow when you are making the call to api/v2/user/password to reset your password, you are providing the old token with it which is blacklisted as you already refreshed it. Here are the APIs that are commonly used for auth and password reset.
POST api/v2/user/session : Used for authentication. Can be called without a JWT (Open API). This will authenticate the user, create a session and return the JWT.
GET api/v2/user/session : Used for retrieving current user session. Requires passing the JWT for the user session you are retrieving. IT DOESN'T CREATE A NEW SESSION OR JWT. IT VERIFIES THE JWT PASSED THAT YOU PASSED IN AND RETURNS THE ASSOCIATED USER INFORMATION FOR THAT SESSION.
PUT api/v2/user/session : Used for refreshing a JWT that is still inside the refresh TTL window. Requires passing the JWT that is being refreshed. Once a JWT is refreshed it is added to the blacklist.
POST api/v2/user/password: Used for resetting password. Can be (and should be) called without a JWT. When you are resetting a password it is expected that you currently don't have an active session therefore no JWT is required to pass.
If you are still getting the blacklisted error after calling the password reset API without passing any JWT then please provide the full error with the stack trace. Also I would recommend upgrading to the latest version of DF (version 2.9.0 as of 10/17/2017). The latest version has lots of improvements and bug fixes.
I know I'm not sending a token. I will try to get a stack trace. Thanks for the detailed response.
I'm trying to solve this problem detailed in the below forum posts: http://community.dreamfactory.com/t/forgot-password-api/1236/6 http://community.dreamfactory.com/t/password-reset-not-working-after-user-logins/3180 http://community.dreamfactory.com/t/unable-to-change-or-reset-password/4100/
After looking through much of the code, I have found what I believe to be the issue.
Sometimes
JWTUtilities::invalidate($token);
is called to change a token to blacklisted. This function seems to also remove the token from themap
.Othertimes
JWTAuth::invalidate();
is called directly:https://github.com/dreamfactorysoftware/df-core/blob/db355e08a6eb976d6a93caa76f689af04b8ae392/src/Utility/JWTUtilities.php#L177
I think part of the password reset + blacklisted token issue stems from this. Especially when the error stems from here: https://github.com/dreamfactorysoftware/df-core/blob/db355e08a6eb976d6a93caa76f689af04b8ae392/src/Resources/UserPasswordResource.php#L406
Because
invalidateTokenByUserId
will get called via the user model atUser.setPasswordAttribute($password)
.