`linkWith` no longer returns a `sessionToken`

We’ve been using the following code for 3rd party auth via AD for a long time without issues, however, upgrading from v4.10.3 > v5.2.5 breaks the linkWith function, which historically returned a sessionToken (as documented here: JavaScript Developers Guide | Parse.

  let ssoUser = new Parse.User({
    firstName: userData.firstName,
    lastName: userData.lastName,
    email: userData.email.toLowerCase(),
    username: userData.email.toLowerCase(),
  });

  ssoUser = await ssoUser.linkWith(
    'microsoft',
    { authData: authData.microsoft },
    { useMasterKey: true }
  );

  console.log(ssoUser.getSessionToken()) // undefined

Any suggestions are welcome, thank you.

I guess the problem is happening because you are using the { useMasterKey: true } option. Could you please try without it?

Hi @davimacedo , unfortunately not. first I got the error Clients aren't allowed to manually update email verification. So I commented out the emailVerified: true, section, and got the same behaviour - no session token returned from the linkWith function.

Any other thoughts would be very welcome.

Cheers

Maybe you found a bug. Would you be willed to open a PR to the Parse Server project with a failing test case?

More than willing to do that, once I have figured out how. Leave it with me.

Hi @davimacedo I was unable to get the test case to fail, so I have been checking all the differences in my update branch and came across the change in 5.x, which makes directAccess: true by default. I changed this to false in the parse config object and everything miraculously works perfectly.

I’ve been reading up a little on the directAccess changes and I’m not sure I fully understand it, and unsure how much of a performance hit I will have by turning it off (I guess not a hit, but no improvement from 4 > 5).

Do you think there is scope for me to make an improvement to the tests / codebase with this finding, or is it just down to my unique implementation?

It can be something unique to your implementation. Since the directAccess option is making difference on how your code behaves, I assume this code is actually a cloud code function, right? Would you mind to share the cloud code function that you have in place for that?

Sure,

Here is the code:

async LoginUser(request) {
    let user;
    const { username, password, installationId, authData } =
      request.params;

    // If this is an SSO login, it will send { authData: { microsoft: { id, access_token } } }
    if (authData && authData.microsoft) {
      // for now we only support microsoft
      const msalProfile = await getMsalProfile(authData.microsoft.access_token);

      let firstName = msalProfile.givenName;
      let lastName = msalProfile.surname;

      const userData = {
        firstName,
        lastName,
        email: msalProfile.mail || msalProfile.userPrincipalName,
        profession: msalProfile.jobTitle,
      };

      let ssoUser = new Parse.User({
        firstName: userData.firstName,
        lastName: userData.lastName,
        email: userData.email.toLowerCase(),
        username: userData.email.toLowerCase(),
        profession: userData.profession,
        emailVerified: true,
        isSSO: true,
      });

      ssoUser = await ssoUser.linkWith(
        'microsoft',
        { authData: authData.microsoft },
        { useMasterKey: true }
      );

      user = ssoUser;
    } else {
      // Otherwise, this is a username/password login so we proceed as normal
      const userQuery = new Parse.Query(Parse.User).equalTo(
        'username',
        username
      );

      user = await Parse.User.logIn(username, password, { installationId });
    }
    return user;
  }

Is it a trigger or cloud function?

It is a cloud function.

I still believe that it should return the session token without using the useMasterKey option and even from cloud code using directAccess option. Maybe it is a bug on the directAccess option. If you could try once more to write a failing test case in parse server in which you have this option set true and see if you can reproduce. Another idea would be trying something like this:

let ssoUser = new Parse.User({
        firstName: userData.firstName,
        lastName: userData.lastName,
        email: userData.email.toLowerCase(),
        username: userData.email.toLowerCase(),
        profession: userData.profession,
        emailVerified: true,
        isSSO: true,
        authData
      });
await ssoUser.save();

I just tried it out with the REST API with PUT to endpoint <parse-server-url>/users and body:

{
    "authData": {
        "facebook": {
            "id": "...",
            "access_token":	"...",
            "expiration_date": "..."
        }
    }
}

The sign-up was successful and the response included the session token. Parse Server 5.2.7

I suggest you also try it with the REST API, as it cuts out the Parse JS SDK and lets you determine whether there is an issue with the server.

You cannot set some of the values on the _User without using masterkey, emailVerified for example.

I will take a look at what Manuel suggested and see how I get on.

The default is not true in Parse Server 5, it’s still false. The PR you referenced only officially deprecated the default state. The default is scheduled to change from false to true in Parse Server 6.

You cannot set some of the values on the _User without using masterkey, emailVerified for example.

Which values are these? That would be good to know, so maybe try one by one and see which one(s) you cannot set.

Sounds like it could be similar to an issue that occurred with the Swift SDK:

When linking a user, a sessionToken isn’t always provided.

Also, one of the developers found something related to preventLoginWithUnverifiedEmail, in which, depending on what flags preventLoginWithUnverifiedEmail changes, could be causing sessionToken not to be sent:

Also see: