Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate user sessions #3451

Closed
jayjac opened this issue Jan 30, 2017 · 16 comments
Closed

Duplicate user sessions #3451

jayjac opened this issue Jan 30, 2017 · 16 comments

Comments

@jayjac
Copy link

jayjac commented Jan 30, 2017

Hi,
I'm using the iOS SDK.
I have a simple login screen with one username textfield, one password textfield and one login button.
So far, when I tap the button, the user gets logged in with no difficulty.
The only thing I find weird is if the user taps the login button multiple times, a new session object is created for each tap in the mongo database. (so far, I'm just testing things, so the login view controller doesn't disappear, therefore the user is free to tap the login button time and time again).
All the session objects related to the same user have the same installationId and I thought that the previous sessions would be destroyed.
How could I destroy the previous sessions upon logging in if those sessions have the same userId / installationId pair ?

Steps to reproduce

Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable.

  1. Create Login view controller in XCode and add UI elements to send username and password to
    the server
  2. Tap button to log in multiple times
  3. Wait for logins to be completed with success

Expected Results

One session object with the latest userid/installationid would exist in the database as per the documentation:
"Sessions represent an instance of a user logged into a device. Sessions are automatically created when users log in or sign up. They are automatically deleted when users log out. There is one distinct Session object for each user-installation pair; if a user issues a login request from a device they’re already logged into, that user’s previous Session object for that Installation is automatically deleted"

Actual Outcome

A new session object is created every time with no limit.

Environment Setup

  • Server

    • parse-server version : 2.3.2
    • Operating System: MacOS El Capitan
    • Hardware: 16GB RAM
    • Localhost or remote server? Localhost
  • Database

    • MongoDB version: 3.0.6 and 3.4.1
    • Storage engine: mmapv1 and wired-tiger
    • Hardware: 16GB RAM
    • Localhost or remote server? Localhost
@fott1
Copy link

fott1 commented Feb 1, 2017

SessionTokens are now Revocable that means that a user gets a new token each time one of the following actions takes place: signup, login, upgrade. If a user is logged-in, the last token will have the following property: createdWith.action = login.

Now as far as i tested some cases, if you delete (e.g. calling destroy for that token via cloud, or manually deleting it), the next time the user will try to use the app, the response will be "invalid token", and the user will be logged out.

  • So, in a login attempt, a new token is being created. The old one is not being deleted.
  • If you destroy the last token created in the last login, the user is logged out.
  • Additionally, there is a setting for parse-server where you can extend expiresAt property to any future date you like.

@georgesjamous
Copy link
Contributor

@fott1 but lets say we need to implement password validation before the user modifies his account information in the application. And we ask the user to supply his password then perform a login with the provided password and the existing user's username.

Eventually this action over time will create many unusable session and the Session class with then have thousands of garbage sessions.

How do you propose a way around this ?

@montymxb
Copy link
Contributor

montymxb commented Nov 10, 2017

@georgesjamous this has actually been fixed in #4143, effective in parse-server versions 2.6.1 and up. Must have missed this issue when we applied the change.

Here's the changelog for that release as well. If you can't upgrade to that version of parse you can check out the diff and apply a hotfix as needed, but we recommend you upgrade if possible.

@georgesjamous
Copy link
Contributor

@montymxb we just updated to "parse-server": "2.6.5" Check out the screenshot that I have included, sessions are being created for the same user-intallationid.

We submitted multiple user logins on iOS simulating the time where the user requires to verify his credentials.

screen shot 2017-11-11 at 1 33 06 am

@flovilmart
Copy link
Contributor

what do you use to validate the passwords? The documented options of a beforeSave call? Can you provide more context with code please?

@georgesjamous
Copy link
Contributor

We are login in the user normally on the device for password validation. (iOS)
We are only providing the password and using the current user's username to make sure they match.
As I understand this way should not create multiple sessions. (or am I wrong ?)

[PFUser logInWithUsernameInBackground:[PFUser currentUser].username 
password: password ] continueWithBlock...

also in Cloud code we have completely remove beforeSave and afterSave on '_User' class.

This is our server configuration regarding sessions and email verification.

{
revokeSessionOnPasswordReset : true,
 accountLockout: {
    duration: 10, // duration policy setting determines the number of minutes that a locked-out  account remains locked out before automatically becoming unlocked. Set it to a value greater than 0 and less than 100000.
    threshold: 8, // threshold policy setting determines the number of failed sign-in attempts that will cause a user account to be locked. Set it to an integer value greater than 0 and less than 1000.
  },
  verifyUserEmails: true,
  emailVerifyTokenValidityDuration: 2 * 60 * 60, //2 hours
  preventLoginWithUnverifiedEmail: false 
}

@flovilmart
Copy link
Contributor

Can you provide the logs when running with VERBOSE=1 for those calls that make duplicated sessions? I have trouble understanding how this is possible, given the tests and code we have in place to prevent it in the 1st place.

@georgesjamous
Copy link
Contributor

georgesjamous commented Nov 11, 2017

We just wanted to know at what point in lib/Rest.Write.js in parse-server the new session token is created. Its being created in _this.runDatabaseOperation before reaching the _this.createSessionTokenIfNeeded();
I believe on line 1024 at

// Run a create
   return this.config.database.create(this.className, this.data, this.runOptions).catch(function (error) {

Hope this makes sense to you since at our stage we still don't have a clear grasp about the internal of parse-server, we are still messing around with it and learning.


Current User Session Token just before calling login is r:7272500227b691e73ea52d863a6de037
Log:

verbose: REQUEST for [GET] /xeronium/login: {
  "username": "+9613XXX166",
  "password": "********"
} method=GET, url=/xeronium/login, host=185.XXX.XX.6:9091, x-parse-client-version=i1.15.3, accept=*/*, x-parse-application-id=ykG, x-parse-client-key=D2Jh04F, x-parse-installation-id=40ed0f41-b89d-4881-a85d-fccf12081b95, accept-language=en-us, x-parse-os-version=11.1 (15B93), accept-encoding=gzip, deflate, content-type=application/json; charset=utf-8, content-length=62, user-agent=PointAutos/1 CFNetwork/889.9 Darwin/17.2.0, connection=keep-alive, x-parse-app-build-version=1, x-parse-app-display-version=1.0, username=+9613XXX166, password=********
verbose: RESPONSE from [GET] /xeronium/login: {
  "response": {
    "objectId": "oM5SKwmaeG",
    "note": {
      "watchlist": {
        "date": "1993-08-09T12:26:00.931Z",
        "count": 0,
        "limit": 3
      }
    },
    "public": {
      "username": "+9613XXX166",
      "lastName": "Jamous",
      "firstName": "Georges"
    },
    "searchKeywords": [
      "georges",
      "jamous",
      "9613XXX166"
    ],
    "firstName": "Georges",
    "country": "LBN",
    "origin_country": "LBN",
    "lastName": "Jamous",
    "username": "+9613XXX166",
    "useLessData": false,
    "listingsCount": 4,
    "createdAt": "2017-11-10T18:30:15.968Z",
    "updatedAt": "2017-11-10T23:49:55.884Z",
    "_failed_login_count": 0,
    "terms_version_number": "1.2",
    "ACL": {
      "*": {
        "read": true
      },
      "oM5SKwmaeG": {
        "read": true,
        "write": true
      }
    },
    "sessionToken": "r:b2d7c72cc2bf32a47c905a844c9c8a23"
  }
} objectId=oM5SKwmaeG, date=1993-08-09T12:26:00.931Z, count=0, limit=3, username=+9613XXX166, lastName=Jamous, firstName=Georges, searchKeywords=[georges, jamous, 9613XXX166], firstName=Georges, country=LBN, origin_country=LBN, lastName=Jamous, username=+9613XXX166, currency=USD, watchloweprice=true, watchlessmilage=true, useLessData=false, listingsCount=4, createdAt=2017-11-10T18:30:15.968Z, updatedAt=2017-11-10T23:49:55.884Z, _failed_login_count=0, terms_version_number=1.2, read=true, read=true, write=true, sessionToken=r:b2d7c72cc2bf32a47c905a844c9c8a23

@flovilmart
Copy link
Contributor

flovilmart commented Nov 11, 2017

So basically you're calling login over and over, because you want to validate that the user is able to provide a valid password, even though the current user is currently logged in in the app, and you expect the session to stay the same or a new session to be created?

I may suggest you use shorter sessions, and upon app launch check if the session is still valid, if not valid, you just have to re-present the login controller.

@georgesjamous
Copy link
Contributor

Sometimes you need to verify the user's credentials even though the user is
logged in without login him out first, to secure some pages, don't you agree ?

It doesn't matter if a new session is created or the session is still the same, our concern is only that over time with thouthands of users, _Session class could get massive very quickly.

What I do expects, and I think it would be better over time that if either,
the session stays untouched (just validate username and password)
or create a new session object and remove the old one.

In the end Each Installation-User must only have one _Session object.

@flovilmart
Copy link
Contributor

I managed to write a test that encompasses this particular use case that was obviously overlooked.
It currently generates a new session token without destroying the old one.
For now it generates a new session token but never invalidates the old ones. The issue is located in UserRouter as it's creating the session directly in there. it's part of a refactor I wanted to put a while ago. On a fix now.

@junal
Copy link

junal commented Mar 2, 2018

@flovilmart Is there anyway an admin user can remotely log out another user. For example, admin approve certain things for user and user cannot see the update until they sign out and sign in again. How to overcome this situation?

@flovilmart
Copy link
Contributor

You can log a user out by deleting it’s session object.

@adlondon
Copy link

adlondon commented May 4, 2018

Was this issue resolved? Having the same problem in 2.7.4

@flovilmart
Copy link
Contributor

The issue was closed 6 months ago, please open a new issue filling the issue template as much as you can, all information is critical to get us quickly on it! Thanks!

@adlondon
Copy link

adlondon commented May 4, 2018

Thanks for the quick response, will do

UnderratedDev pushed a commit to UnderratedDev/parse-server that referenced this issue Mar 21, 2020
…y#4337)

* Adds failing test for parse-community#3451 (on multiple logins)

* Factor sessionDestruction as part of Session creation flow in RestWrite

* nits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants