flutterparse-platformparse-cloud-codeback4app

How can I assign ParseUser object with session token when performing a server based login? - Parse Back4App


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.


Solution

  • 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.

    Link here

    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