`linkWith` not returns a `sessionToken` in CloudCloud
I'm encountering an issue with linkWith in Cloud Code on Parse-Server 7.2. When linking a user with a third-party provider, the method does not return a sessionToken as expected. This breaks the intended behavior, where the user should be logged in automatically after linking.
Steps to Reproduce Here is the code snippet that demonstrates the issue:
Parse.Cloud.define(
'auth',
async req => {
const { authData, nickname, email } = req.params;
const authName = Object.keys(authData)[0];
if (!authName) {
throw new Parse.Error(1000, {
code: 1001,
msg: 'invalid authData',
});
}
const provider = getAuthProvider(authName, authData?.[authName]);
if (!provider) {
throw new Parse.Error(1000, {
code: 1001,
msg: 'invalid authData',
});
}
let hasUser;
if (email) {
const query = new Parse.Query(Parse.User);
query.equalTo('email', email);
hasUser = await query.first({ useMasterKey: true });
req.log.info('hasUser', hasUser);
}
const user = new Parse.User();
if (!hasUser) {
user.set('email', email);
user.set('nickname', nickname);
}
return user.linkWith(provider, authData?.[authName]);
},
{
fields: {
authData: {
required: true,
type: Object,
},
nickname: {
required: true,
type: String,
},
email: {
type: String,
options: email => {
return /^[\w-\.]+@([\w-]+\.)+[\w-]{2,12}$/.test(email);
},
},
},
}
);
Attempt to link the user with a provider using linkWith. Observe that no sessionToken is returned.
Environment Parse Server Version: 7.2
🚀 Thanks for opening this issue!
I was unable to replicate, here is the test that I wrote:
it('link with provider should return sessionToken', async () => {
const provider = getMockFacebookProvider();
Parse.User._registerAuthenticationProvider(provider);
const user = new Parse.User();
user.set('username', 'testLinkWithProvider');
user.set('password', 'mypass');
await user.signUp();
Parse.Cloud.define('linkWith', req => {
const user = req.user;
const authData = req.params.authData;
return user.linkWith('facebook', authData);
});
const httpResponse = await request({
method: 'POST',
url: Parse.serverURL + '/classes/_User',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-REST-API-Key': 'rest',
'Content-Type': 'application/json',
},
body: { authData: { facebook: provider.authData } },
})
expect(httpResponse.data.sessionToken).toBeDefined(); // r:f0032f6f62b6b42858db3faa6dc7520b
expect(httpResponse.data.objectId).toBeDefined(); // UXKOqBmJ3Z
});
@dblythy could you please add this test in a PR? In case @puny-d doesn't respond we'll at least add the test and close the issue.
Just to add to this discussion. I did something like this recently as well. Observed that it did not return the sessionToken.
Note: This only happens if directAccess is set to true. If it's false, linkWith works as intended and returns a sessionToken.
Doing following workaround via the Rest API currently to make it work with directAccess: true:
export async function loginUser<T>(
authData: T,
provider: string,
): Promise<Parse.User> {
const url = `${SERVER_URL}/users`;
const data: AuthDataPayload<T> = {
authData: {
[provider]: authData,
},
};
try {
const response = await axios.post(url, data, {
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Revocable-Session': '1',
'Content-Type': 'application/json',
},
});
const userData = {
className: '_User',
...response.data,
};
return Parse.User.fromJSON(userData);
} catch (error) {
console.error(
'Error logging in user:',
error.response ? error.response.data : error.message,
);
throw error;
}
}
That's an important hint, I would ask @dblythy to modify the test to run with directAccess: true, but that may not be so easy. This may be related to https://github.com/parse-community/parse-server/issues/8808.
Yeah I tried to edit the test / test suite to no avail. Let me see if I can replicate with the example server
This is intended behavior, if you want the sessionToken you will have to pass an installationId to linkWith. This goes for login, signup etc.
https://github.com/parse-community/parse-server/blob/257be691280081677689cc585e39376a410dba0c/src/RestWrite.js#L974-L979
Note: This only happens if directAccess is set to true. If it's false, linkWith works as intended and returns a sessionToken.
What does that have to do with directAccess then?
Setting directAccess set the installationId to be cloud. Which prevents sessionTokens from being created in my previous comment https://github.com/parse-community/parse-server/issues/9518#issuecomment-2771229712
https://github.com/parse-community/parse-server/blob/257be691280081677689cc585e39376a410dba0c/src/ParseServerRESTController.js#L13-L15
I reopened this issue because I wonder:
a) Is this behavior documented, and if not, should it be? b) Why does it require an installationId to get a sessionToken with login / linkWith? What does the installation have to do with the user logging in? Maybe this restriction should be removed if it's not technically necessary?
a) Is this behavior documented, and if not, should it be?
No idea, it should be
b) Why does it require an installationId to get a sessionToken with login / linkWith? What does the installation have to do with the user logging in? Maybe this restriction should be removed if it's not technically necessary?
What is the installationId? It's a unique identifier per device. Passing it is what connects the loggin user to a device. The server isn't a device.
Can we remove it? The code is commented by our ancestor saying it's dangerous to have session in cloud environment.
Reading through the discussion, I realized that a session object has an installationId field. This seems to be a Parse Server concept, likely embedded deeply in the code base. The discussion describes similar use cases as this one here, so it may be worth a look, @puny-d, @jimnor0xF.
The question then remains why this behavior should be different whether directAccess is true of false. It's the same Cloud Code environment after all. It's also not documented in the options and not really intuitive.
Maybe the solution could be:
- Fix Parse Server to always set the default installation ID to
cloudwhen in Cloud Code, regardless ofdirectAccess, so a session token is never returned and the behavior is uniform. The argument is simply that a session is always associated with an installation ID, hence it's required. For use cases in which a session token should be created, an installation ID can be passed as an argument. - Add this explanation to the Cloud Code docs; this is currently neither documented in the Cloud Code nor in the Parse JS SDK docs.
The code is commented by our ancestor saying it's dangerous to have session in cloud environment.
Yeah, but I still don't quite understand why it's "dangerous" to use in Cloud Code though. I do remember reading that for some methods like logIn sessions are saved on disk.
I don't know if that applies to linkWith. If that is the case, I could see why it is dangerous since if these things pile up at runtime on the server side, it's not good.
https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html#logIn https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html#.enableUnsafeCurrentUser
Without pre-empting @dplewis's response, I think that was a point of view that came up during the transition from hosted parse.com to open source Parse Server. In parse.com it was valid to use Parse.User.current(); in Cloud Code as each request created a separate instance of Cloud Code without shared states between requests. In open source Parse Server that was not the case anymore, as the server is continuously running Cloud Code with a persistent state. In very early versions of open source Parse Server, it was possible to use Parse.User.current();, which caused the expected issues. It was at that time that the combination of session token in Cloud Code was considered "dangerous", in other words it was a complex issue to address during the transition. I don't see this as an issue anymore today. You just need to know how to handle the session token. The other aspect is that Parse Server entangles session and installation, for some (historic?) reason, so if a session by design always requires an installation ID, then that's just how Parse Server works at the moment.
- Fix Parse Server to always set the default installation ID to
cloudwhen in Cloud Code, regardless ofdirectAccess, so a session token is never returned and the behavior is uniform. The argument is simply that a session is always associated with an installation ID, hence it's required. For use cases in which a session token should be created, an installation ID can be passed as an argument.- Add this explanation to the Cloud Code docs; this is currently neither documented in the Cloud Code nor in the Parse JS SDK docs.
I think these suggestions are good then.
@mtrezza what is the action needed here?
Wouldn't be changing the behaviour to pass "cloud" in as a default installation be breaking ?
This would be a breaking change. Technically the default is cloud as direct access is defaulted to true
So should we hold off until Parse Server 9?