How to run a parse cloud code if you only set a client key - Flutter SDK

I’ve been trying to solve this for almost 3 days now. According to the documentation of the Parse Server regarding the security. It says that the master key should only be accessed on the server-side, not on the client-side. So I’ve added a client key on my parse configuration and added it also to my parse initialization on the app. Now everything seems working but when I’ve tried to run a cloud code, it says that my request is unauthorized, I’ve read all of the guides and documentation that are connected to parse cloud code, but I cannot seem to find or understand how to get this working. How can I get this working?

Have you set an appId? I believe unauthorized can throw if the appId is incorrect.

Also what SDK are you using to call the cloud function?

I am using the Flutter Parse SDK. and yes my parse config has an app id

await Parse().initialize(‘my appid’, ‘https://api.skoodapis.com/parse’,

  // masterKey: 'masterkey',

  clientKey: 'myclientkey',

  debug: true,

);

Hmm. Would you mind sharing your Parse Server configuration?

This is my config

const config = {
databaseURI: process.env.DATABASE_URI || 'mongodb://userName:[email protected]:27017/app_db',
cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
appId: process.env.APP_ID || 'App-79c1451f-7b7a-4fbf-852f-5d44dbcc2a69',
masterKey: process.env.MASTER_KEY || 'mysecretmasterkey',
serverURL: process.env.SERVER_URL || 'https://apis.com/parse',
cacheAdapter: redisCache,
liveQuery: {
  classNames: ["Orders", "Categories", "Addresses", "Customers"],
redisURL: 'redis://:password@localhost:6379'
},
clientKey: process.env.CLIENT_KEY || 'password',
appName: 'Skood',
emailAdapter: {
  module: '@parse/simple-mailgun-adapter',
  options: {
    fromAddress: '[email protected]',
    domain: 'mydomain.net',
    apiKey: 'key-abcs123820'
  }
}

};

Can you try on Flutter:

await Parse().initialize('App-79c1451f-7b7a-4fbf-852f-5d44dbcc2a69', ‘https://api.skoodapis.com/parse’,

  // masterKey: 'masterkey',

  clientKey: 'password',

  debug: true,

);

Yes I’ve done this but it still no luck.

I’m not familiar with the Flutter SDK, so sorry if I’m not being much use.

Can you try this request (change FUNCTION NAME), and let me know what result you get?

curl -X POST \
  -H "X-Parse-Application-Id: App-79c1451f-7b7a-4fbf-852f-5d44dbcc2a6" \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://api.skoodapis.com/parse/functions/FUNCTION_NAME

Okay so here’s what I’ve found so far.

Problem: Cannot run cloud functions if you are only supplying a client key on the initialization of the parse on Flutter App.

What I did:

  1. Added a client key on my config on the Parse Server.
  2. Changed the masterkey parameter to client key on the initialization of parse on my flutter app.

Before Adding the client key on my parse server config file and before changing the masterkey parameter to client key on the initialization on my flutter app. Everything is working. My configs are working.

But then after adding the changes. Cloud function are not working anymore.

Tried to revert the changes I made on the Parse initialization to use the master key, but it’s still not working.

Tried to removed the client key I’ve declared on my Parse Server config… Now its working again.

I don’t seem to understand if this is a default behavior or not.

Should I just keep my masterkey present on the client side in order for this to work? Or am I doing something wrong here?

Ok, thank you for the detail and explaining your problem.

Are you saying that when you add a client key to your Parse Server config, the rest of your Parse code (queries, objects, etc), work, but your Cloud functions called from the Flutter SDK do not?

And you should never expose your masterKey on the client side. We’ll work out why this isn’t working, but if your solution is to expose your masterKey, you’re exposing your whole configuration.

Also that worth mentioning that I typically use the JS, iOS, and Android SDKs, and I’ve never had to set clientKey.

Yes you’re on point. It’s just on the Flutter SDK cloud functions doesn’t seem to work if you are not exposing your masterkey on the client side.

UPDATE

Prior to now, I tried not exposing my masterkey on the client side so the initialization looks like this

await Parse().initialize('App-79c1451f-7b7a-4fbf-852f-5d44dbcc2a69', 'https://api.skoodapis.com/parse',
// masterKey: 'masterKey',
// clientKey: 'clientKey',
  debug: true,
  autoSendSessionId: true,
  liveQueryUrl: 'wss://api.skoodapis.com',
);

I don’t know how and why but the queries, objects, and cloud functions are still working. Can you please elaborate this to me.

At your HTTP listener in your server config, can you add this:

 httpServer.listen(port, async function () {
    console.log('parse-server-example running on port ' + port + '.');
    const result = await Parse.Cloud.run('FUNCTION_NAME');
    console.log(result);
});

This way we can isolate whether the cloud function is registered properly on your server.

If result returns the expect result, then it’s related to the Flutter SDK, and we might have to wait until someone more familiar with the SDK can add some insight.

This is the output on my console log:

[email protected] start /home/luke/parse-server-example
node index.js
parse-server-example running on port 1337.
info: Parse LiveQuery Server starts running
info: Ran cloud function getServerTime for user undefined with:
  Input: {}
  Result: {"__type":"Date","iso":"2021-03-08T07:44:55.837Z"}     {"functionName":"getServerTime","params":{}}
2021-03-08T07:44:55.837Z
info: Create new client: 2e15187b-be20-44d9-9c88-3b87be3ff2ab
info: Create new client: b02014fc-652f-4605-a27c-85b8065720dd
info: Create new client: 793e60ac-addf-4fe8-ae4d-6447ab59a9fb

Oh I see, let’s say for example I will launch this app into production, will it affect the security of server?

Ok great. That’s good that the cloud function ran as expected. But strange that they aren’t working with flutter. I’m not sure why.

@phillwiggins would you have any insight to add here?

Yes, the problem here is that cloud functions won’t run if there is a client key declared on the Parse Server Config.

Yes, the problem here is that cloud functions won’t run if there is a client key declared on the Parse Server Config.

And just to confirm, your clientKey in your flutter init matches the clientKey in your server config?

Oh I see, let’s say for example I will launch this app into production, will it affect the security of server?

Yes, if the masterKey is in publicly accessible code (people can decompile / inspect / record network calls), effectively all data is compromised.

This is because masterKey overrides ACLs. So, for example, if you wanted to get all users, delete all users, etc, if you have the masterKey you can, regardless of whether you’ve restricted access using ACLs or CLP. This is why we generally recommend having a strong masterKey, only using your masterKey on your server, and rotating your masterKey every so often.

Yes it is.

This is what I am afraid of, that’s why I’m trying to figure out and understand the behavior/connection between cloud functions, the keys, and the Server.

Thanks in advance!

We are currently developing a startup using Parse as our Backend, and we migrated from Firebase to Parse because of the result of projections dropping. There is too much cost. Currently I am happy with my experience. I just need to understand it more.

1 Like

Hi @loooookiezxc,

I am also working with the SDK Flutter, and using Cloud Functions perfectly.

You can use them by informing ClienteKey only.

In your Cloud Function methods, when you need to perform any action where the user does not have permission, you must define the use of MasterKey. I will describe an example to be able to demonstrate:

Parse.Cloud.define('updatepin', async (request) => {
	
	var user = request.user;
		
	if(!user) {
		throw 'Not Authorized.';
	}
	
	var uid = request.params.uid.toString();
	
	var ob = new Parse.Object('numbers');
	ob.id = uid;
	ob.set('used', true);
	await ob.save(null, { useMasterKey: true }).then(function(){
		return 'success';
	}, function(error){
		throw error;
	});
	
	return uid;
});

In the following example for the “numbers” class, no customers are allowed, but it is necessary to update information. I use the Cloud Function and go through the “uid” parameter, that is, the “objectId” of the document, and set the field I want as “true”. The important thing is to inform that this action must be done with masterKey in the line of saving this change.

await ob.save(null, { useMasterKey: true }).then(function(){

Only that, in Cloud Function just inform that the action must be done with Master Key. I hope I have helped.

1 Like