What I want:
A fully operational server side login & logout ability with client setting authenticated user properly.
Why do I want this?
My purpose is to lock down all server classes fully, client will have zero trust and zero access. Everything will happen through cloud code. Currently I am doing this for all my code except session actions but I recently decided to make the overly protective move of doing this for my login/logout actions also. I will be applying encryption to login credentials sub-https both ways.
Problem:
I can't seem to reassign ParseUser when I get a result from the server. I cannot then make any cloud calls or even logout because the ParseUser is not a full ParseUser object including sessionToken. When using normal Parse server SDK the login() method reassigns the ParseUser object respectfully. How can I do this through cloud code client side?
EDITED CODE - Updated in accordance with a github issue I got help with to solve this problem.
/// Cloud login method - SERVER SIDE
Parse.Cloud.define("login", async function(request, response) {
const username = request.params.username;
const password = request.params.password;
// Login
try{
let user = await Parse.User.logIn(username, password);
if (user) return user
} catch (error) {
throw new Error(error.message);
}
});
/// Cloud login method - CLIENT SIDE
Future<ParseResponse> login(WidgetRef ref, String username, String password) async {
ParseUser user = ParseUser(username, password, null);
final ParseCloudFunction cloudLogin = ParseCloudFunction('login');
final Map<String, dynamic> credentials = <String, dynamic>{
'username': user.username,
'password': user.password,
};
final ParseResponse loginAttempt = await cloudLogin.execute(parameters: credentials);
/// ATTEMPT 1
ref.read(authenticationServices).setAuthorizedUser(user);
/// ATTEMPT 2
ref.read(authenticationServices).setAuthorizedUser(loginAttempt.result);
return loginAttempt;
}
Result:
ATTEMPT 1:
SUCCESSFUL LOGIN PAYLOAD
Payload: {
username: 001,
email: 0001@gmail.com,
name: 001,
createdAt: 2022-01-23T05:04:42.888Z,
updatedAt: 2022-01-23T05:04:42.888Z,
ACL: {*: {read: true},
KMq2UWO7h8: {read: true, write: true}},
sessionToken: r:5d3c7409d572d30bad3e40db3586304a,
objectId: KMq2UWO7h8,
__type: Object,
className: _User
}
LOGOUT FAILURE
flutter: {"className":"_User","username":"001"} <--------- ParseUser object does not get mutated on login with authorized object
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: Null check operator used on a null value
#0 ParseUser.logout
ATTEMPT 2:
LOGIN FAILURE
error:
type '_InternalLinkedHashedMap<String, dynamic>' is not a subtype of ParseUser
I am posting an answer to this question below.
I got some help in answering this question on GitHub. I have now achieved a fully secure system where I can apply a sub-https layer of encryption to all cloud code and all application actions including login/logout/signup etc.
I was trying to set user object explicitly like so:
late ParseUser? _authorizedUser;
ParseUser? get authorizedUser => _authorizedUser;
void setAuthorizedUser(Map<String, dynamic> user) {
_authorizedUser = ParseUser(
user['username'],
user['password'],
user['email'],
)
..set('sessionToken', user['sessionToken'])
..set('objectId', user['objectId'])
..set('className', user['className'])
..set('name', user['name'])
..set('createdAt', user['createdAt'])
..set('updatedAt', user['updatedAt'])
..setACL(user['ACL']);
}
But even this was not enough, I got an error traced back to ParseCoreData().sessionId being null.
Turns out the solution is to call "setSessionId" after setting user data.
ParseCoreData().setSessionId(user['sessionToken']!);
Thanks again to RodrigoSMarques