Repository navigation
Invalid legacy session token after ParseUser.enableRevocableSessionInBackground #2845
Description
Issue Description
Our Android users with multiple devices are permanently getting code=209, message=invalid legacy session token on all but one of their devices.
- Users had several devices with our app installed
- Our app was pointed at parse.com (not yet parse-server)
- The users had a single ParseUser record, they were logged into it on each device, our app was not using revocable sessions
- We updated our Android app, to switch to parse-server
- The Android app on startup (every time) calls
ParseUser.enableRevocableSessionInBackground()
What we see is:
a. The first device to update works fine
b. The other devices do not
So despite the other devices calling ParseUser.enableRevocableSessionInBackground(), they are still getting code=209, message=invalid legacy session token on all parse operations.
The only work around we've found is to get them to uninstall and reinstall the app on those devices.
Steps to reproduce
- Install 0.1.229 of our Android app on two devices, this points to
parse.com - In the Account section, on one app, create an account
- In the Account section, on the other app, login to that account
- Optional: create an invoice one device, confirm it syncs to other device
- Update device 1 to 0.1.232, now the app points to our
parse-server, and callsParseUser.enableRevocableSessionInBackground() - See everything works fine
- Update device 2 to 0.1.232
- Get exception
com.parse.ParseRequest$ParseRequestException: invalid session tokenwhen the app starts up, when it tries to callParseUser.getCurrentUser().saveInBackground()
Expected result: apps on both devices work properly, and can do basic parse operations involving the ParseUser like saving it, saving other objects pointing to it etc
Environment Setup
- Server
- parse-server version 2.2.22
- using Express
- Operating System: Android
- Localhost or remote server? Heroku
- Database
- Hosted at mLab
- Android app
- parse sdk verison 1.13.1
Code
Before (parse.com): https://gist.github.com/alexblack/a2c8bdde59608e85f192448f8e4dde84
After (parse-server): https://gist.github.com/alexblack/8fd376d54c1535a5b8777cbbef4b82df
Logs/Trace
Exception: http://crashes.to/s/16792fdcd3d
{
"client": {
"name": "raygun-node",
"version": "0.8.5"
},
"groupingKey": "code209messageinvalidsessiontoken",
"error": {
"message": "code=209, message=invalid session token"
},
"request": {
"hostName": "invoice-simple.herokuapp.com",
"url": "/parse/batch",
"httpMethod": "POST",
"ipAddress": "::ffff:10.186.80.249",
"queryString": {},
"headers": {
"host": "invoice-simple.herokuapp.com",
"connection": "close",
"x-parse-os-version": "5.1",
"x-parse-app-build-version": "2000359",
"x-parse-client-version": "a1.13.1",
"x-parse-app-display-version": "0.1.232",
"x-parse-installation-id": "d3ca7c2a-60a9-4f44-9d46-d9391940d1cc",
"user-agent": "Parse Android SDK 1.13.1 (com.aadhk.woinvoice/2000359) API Level 22",
"x-parse-session-token": "voARgYrfgzc1SYKF7gm19Bmc8",
"x-parse-application-id": "F8pgJyHm8jxQhXxYnpdEzBTxLP2Nhu68JLtmek3y",
"content-type": "application/json",
"accept-encoding": "gzip",
"x-request-id": "1318fd32-036f-48b0-9ae0-a7fd2a7260b3",
"x-forwarded-for": "114.125.12.147",
"x-forwarded-proto": "http",
"x-forwarded-port": "80",
"via": "1.1 vegur",
"connect-time": "0",
"x-request-start": "1475892231454",
"total-route-time": "0",
"content-length": "499"
},
"form": {
"requests": [
{
"method": "PUT",
"path": "/parse/classes/_Installation/UslVQbptng",
"body": {
"pushType": "gcm",
"deviceToken": "APA91bGFWQgXPnZoI6a4M369qGpKVfCrfLx0ajdSV0aihPCxwSWuXO-XtyB_TvnvyhiKO6eGNT5_VmEytJDP4CUhF_W-hNI6bXtKpJyihHm1XupvtUXz430fYwJYCBbaXOFYDOk4xoIa",
"appVersion": "0.1.232",
"parseVersion": "1.13.1",
"objectId": "UslVQbptng"
}
},
{
"method": "PUT",
"path": "/parse/classes/_User/3Ms85kJUin",
"body": {
"accountId": "9caae171f3654c95a7bfcba87c8b8639",
"countryCode": "id",
"objectId": "3Ms85kJUin"
}
}
]
}
},
"machineName": "33f76be6-2c44-4a29-94db-eb8740cd37ba",
"environment": {
"osVersion": "Linux linux 3.13.0-95-generic",
"architecture": "x64",
"totalPhysicalMemory": 64426360832,
"availablePhysicalMemory": 3063279616,
"utcOffset": 0,
"processorCount": 8,
"cpu": "Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz"
},
"userCustomData": {},
"user": {},
"tags": [],
"OccurredOn": "2016-10-08T02:03:51.48Z"
}
Caused by: org.json.JSONException: Value {"code":209,"error":"invalid session token"} of type org.json.JSONObject cannot be converted to JSONArray
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONArray.<init>(JSONArray.java:96)
at org.json.JSONArray.<init>(JSONArray.java:108)
at com.parse.ParseRESTObjectBatchCommand.onResponseAsync(ParseRESTObjectBatchCommand.java:163)
... 13 more
Inner throwable #1: com.parse.ParseRequest$ParseRequestException: bad json response
at com.parse.ParseRequest.newTemporaryException(ParseRequest.java:290)
at com.parse.ParseRESTObjectBatchCommand.onResponseAsync(ParseRESTObjectBatchCommand.java:167)
at com.parse.ParseRequest$3.then(ParseRequest.java:137)
at com.parse.ParseRequest$3.then(ParseRequest.java:133)
at bolts.Task$15.run(Task.java:917)
at bolts.BoltsExecutors$ImmediateExecutor.execute(BoltsExecutors.java:105)
at bolts.Task.completeAfterTask(Task.java:908)
at bolts.Task.continueWithTask(Task.java:715)
at bolts.Task.continueWithTask(Task.java:726)
at bolts.Task$13.then(Task.java:818)
at bolts.Task$13.then(Task.java:806)
at bolts.Task$15.run(Task.java:917)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: org.json.JSONException: Value {"code":209,"error":"invalid session token"} of type org.json.JSONObject cannot be converted to JSONArray
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONArray.<init>(JSONArray.java:96)
at org.json.JSONArray.<init>(JSONArray.java:108)
at com.parse.ParseRESTObjectBatchCommand.onResponseAsync(ParseRESTObjectBatchCommand.java:163)
at com.parse.ParseRequest$3.then(ParseRequest.java:137)
at com.parse.ParseRequest$3.then(ParseRequest.java:133)
at bolts.Task$15.run(Task.java:917)
at bolts.BoltsExecutors$ImmediateExecutor.execute(BoltsExecutors.java:105)
at bolts.Task.completeAfterTask(Task.java:908)
at bolts.Task.continueWithTask(Task.java:715)
at bolts.Task.continueWithTask(Task.java:726)
at bolts.Task$13.then(Task.java:818)
at bolts.Task$13.then(Task.java:806)
at bolts.Task$15.run(Task.java:917)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Activity
alexblack commentedon Oct 9, 2016
We've been unable to repro this on iOS
[-]code=209, message=invalid legacy session token[/-][+]Invalid legacy session token after ParseUser.enableRevocableSessionInBackground [/+]alexblack commentedon Oct 16, 2016
Actually, we are seeing this on iOS too: http://crashes.to/s/743707c1b9c
Non-fatal: Error Domain: Parse Code: 101 NSLocalizedDescription: invalid sessionflovilmart commentedon Oct 16, 2016
Can you provide the server logs associate with that issue (when running with VERBOSE=1) and the steps to reproduce?
alexblack commentedon Oct 16, 2016
Server logs - yeah I can probably do that.
Steps to repro - I did provide some, are they not acceptable?
flovilmart commentedon Oct 16, 2016
I'll know more with the server logs.
alexblack commentedon Oct 20, 2016
This issue has now affected at least 1,151 of our users on iOS http://crashes.to/s/95c28529e47 and 1,245 users on Android http://crashes.to/s/f1ea38ce61c.
Logs:
Parse-serverlogs: http://pastebin.com/raw/MpDjBQwFGradlelogs: http://pastebin.com/raw/pNHA0kNrParse-serverlogs: http://pastebin.com/raw/Z8AdaWkqGradlelogs: http://pastebin.com/raw/u5BrUsS7As you can see there is basically no info in the heroku logs for
parse-serveron what went wrong for device 2... any ideas?VERBOSEis on as you can see in the logs from device 1flovilmart commentedon Oct 20, 2016
I see that in your second logs, the server response is 400 (http://pastebin.com/raw/Z8AdaWkq). I don't believe this is the right server being hit as it doesn't even seem to pass the heroku router. There's not log involving parse-server failing to upgrade your token or else.
alexblack commentedon Oct 20, 2016
Its the same server. I'm watching it live with
heroku logs --tailflovilmart commentedon Oct 20, 2016
ok, so I see in the cradle logs:
invalid legacy session tokenin that case you should prompt the user to login again. Unless you're 100% sure the session token is still on the User object, in which case this is a problem in the code.See the test suite: https://github.com/ParsePlatform/parse-server/blob/ad707457be13f177e4a0dc481c990ddbd2df8d80/spec/RevocableSessionsUpgrade.spec.js#L72
alexblack commentedon Oct 20, 2016
Yes, thats the issue we're facing, see the issue desc above. Yes, that works around the issue.
Please help fix the issue.
parse-serveris doing a terrible job at logging, no error logged.parse-servershould not respond with invalid session token in this scenario. it should accept the requests.flovilmart commentedon Oct 20, 2016
It should not accept the requests, as you're passing an explicitly bad session token... This is to match the behaviour with parse.com.
We should never let a bad session token make request.
That's not a workaround, that's the expected behaviour...
alexblack commentedon Oct 20, 2016
I'd appreciate some context with your comments. We're using the Android and iOS SDK, they are passing the token, not us.
What did we do wrong? I don't think we did anything wrong. But if you do, please just specify what it is.
alexblack commentedon Oct 20, 2016
Also, please respond to my point about
parse-serverdoing a terrible job logging. This is not helpful:alexblack commentedon Oct 20, 2016
Please elaborate. We migrated from
parse.comtoparse-server, for any of users with multiple devices, the first device to migrate works fine, the second one receives the 209 invalid session token error.This has left thousands of our users screwed. What did we do wrong that causes our user's second devices to get this error?
flovilmart commentedon Oct 20, 2016
This is an open source project, improvements and PR are welcome.
You're not handling the error message that comes back from the server when you want to upgrade a session token that doesn't exist in the database. The legacy session token could be removed from your users for many reasons like an upgrade from another device etc...
Once the session token upgrade took place, it's removed from the user, therefore, rendering it unusable. That's the main difference between legacy session tokens and revocable session tokens.
flovilmart commentedon Oct 20, 2016
AND BTW, parse.com encouraged upgrading all your sessions to revocable sessions since march 2015 for security reasons.
Legacy session tokens are shared across all devices where the user is logged in, revocable session tokens are unique per session (one per login). All get destroyed upon password reset etc...
alexblack commentedon Oct 20, 2016
Again, i'd really appreciate some context. I get that tokens can become invalid, and I get how to handle them.
Why are the tokens from our users's second devices invalid, but the first are fine?
alexblack commentedon Oct 20, 2016
Is this helpful? We're in this situation now. Thanks for the lecture.
flovilmart commentedon Oct 20, 2016
@alexblack i'm taking on my own time to try to answer you, so please, if you'd show more patience that would probably help.
Also, like I said, when you see the error that you have on the client, you should ask the user to login again, that will generate a proper session for the new device.
alexblack commentedon Oct 20, 2016
I appreciate you taking the time to respond. Forgive me if I'm frustrated, I have thousands of users who's apps are not working due to their 2nd device getting the 209 invalid session token error on migrate.
Yes I am aware we should prompt users to re-login. We have now implemented that, but these are apps, and and updates take time to roll out, and many users do not (or have not yet) updated.
For users with legacy tokens, what was the proper migration path then? Our path was:
parse-serverParseUser.enableRevocableSessionInBackground()This works for our user's first devices, but not their other devices. Is this path not supported?
flovilmart commentedon Oct 20, 2016
it doesn't work for the secondary device because the legacy session token gets deleted when generating the new session token. This is a security measure to make sure the legacy session tokens are not used anymore when a new session is created, and similar with how parse.com handled the update.
See the code here: https://github.com/ParsePlatform/parse-server/blob/master/src/Routers/SessionsRouter.js#L78
alexblack commentedon Oct 20, 2016
Damn. So it sounds like we should have stayed on parse.com, called
ParseUser.enableRevocableSessionInBackground(), and after that succeeded, then switch toparse-server. :(flovilmart commentedon Oct 20, 2016
The upgradeSession endpoint wasn't implemented until recently and we didn't verify the session token against legacy session tokens, so on parse-server all of your call were being unauthenticated.
that would have been an option, another option would have been to migrate the session to revocable ones last year when it became 'deprecated'.
abhijeet-aressindia commentedon Nov 8, 2016
@flovilmart I have hit the same wall and raised issue #3003 for same but not get any response yet.
in my case i have stuck on google login implemented with cloud code. Now i have added Parse.User.enableRevocableSession() in cloud code too and new registration just working fine. While Login i am getting.
error: Error generating response. ParseError {
2016-11-08T06:36:39.454856+00:00 app[web.1]: code: 141,
2016-11-08T06:36:39.454857+00:00 app[web.1]: message: '{"code":209,"message":"invalid session token"}' } code=141, message={"code":209,"message":"invalid session token"}
flovilmart commentedon Nov 11, 2016
Parse.User.enableRevocableSession()should not be used in cloud code. Your logins should not occur in cloud code as they set Parse.User.current() and multiple request could concur on that.