Parse-server: Version 2.2.24 and above breaks Parse.File.save() - unauthorized

Created on 5 Dec 2016  路  38Comments  路  Source: parse-community/parse-server

Hi everyone,
I think the the recent changes in v2.2.24 regarding "Better support for checking application and client keys" have broken the save() method for saving Parse.Files.

This is very likely related to my other issue #3051, which I think has since been resolved in v2.2.25.

This code worked in v2.2.23 and below:

//Getting Logo Image from Parse.Cloud.httpRequest
var logoFilename = "logo.png";
var logoFile = new Parse.File(logoFilename, {base64: httpImageFile.buffer.toString('base64')})

//Save logoFile
logoFile.save();

In v2.2.24 and above, I'm now getting unauthorized, even if I pass save({useMasterKey : true}). Again, as in #3051, VERBOSE shows nothing.

I pass all keys in constructor of ParseServer - as @steven-supersolid suggested in #3051.

Saving from the iOS client app however still works...

Most helpful comment

Alight! Closing that! And enjoy the beers for us 馃嵒

All 38 comments

If its cloud based, you need to pass the Javascript key in your config!

How are you initializing the server? Pm2?

@Cliffordwh - As I've said, I'm passing all keys - masterKey, clientKey, restAPIKey, javascriptKey, dotNetKey when creating the constructor.

When I'm testing my Cloud Code Function that contains the Parse.File save(), I'm using curl with the REST endpoint /functions/my_function/ and passing the REST key as X-Parse-REST-API-Key in the header.

There's your problem. Pass the X-Parse-Application-Id as well. That will resolve it!

@Cliffordwh - also passing that, and passing X-Parse-Session-Token if I want to simulate an authenticated user.

mmm thats strange. Sorry to ask, but you 100% sure there is no typo when creating the constructor and the keys correspond?

Could i ask you to curl without the X-Parse-Session-Token to see if you get a response?

@Cliffordwh - I'm 100% sure there is not typo. Not including X-Parse-Session-Token just returns the same unauthorized, as I'm not passing save({useMasterKey : true}) - which apparently is ignored anyway.

@MartinHerman the only last suggestion i can give is to remove client_key from the header.

I found this comment in a thread.

Then tried to include X-Parse-Client-Key in the request header (same value that is passed to server in index.js) and it does not work.
Then I removed the client key in the server initialisation (the code clientKey: process.env.CLIENT_KEY || '',) and it worked !!!

@Cliffordwh - I'm not passing a clientKey in the header. We can't remove it from the ParseServer constructor, as we're also using it for the mobile apps.

Sorry, i thought you said you where passing all keys. im trying to replicate this with little success.

Can you run with VERBOSE=1 to gather the server logs and paste them here?

Also check and make sure you passing serverURL

You need to do this (add null):

logoFile.save(null, {useMasterKey:true});

@Cliffordwh - as I've already stated, VERBOSE shows nothing. The ServerURL is right as well, as all other calls work.

@hedegren - this is the standard format for Parse.Objects - however Parse.File.save() only wants one parameter, as per API reference.

@MartinHerman True, however it seems that you must include the null as first parameter regardless what the docs says. Try it out and see for yourself.

So for one, I believe we may have introduced an issue there, on the keys checking.

Where you do perform you file.save()? I presume in CloudCode.

Also, the file.save should work on any client, as you mentioned, this work fine on the iOS SDK.

Do you have set applicationId, javascriptKey, masterKey and serverURL in your parse-server configuration?

@flovilmart
I might be wrong, but it seems when you pass the {useMasterKey:true} as the first argument, it will be the update fields. When I try it, i get:

Permission denied for action addField on class Jobs.

However, passing null as the first argument and the {useMasterKey:true} as the second, everything works as expected:

// THIS WORKS
var Job = Parse.Object.extend("Jobs");
var job = new Job();

job.set('title', 'test');
job.save(null, {
    useMasterKey: true
}).then(res => {
    console.log("Works"); 
}).catch(error => {
   console.log(error); 
});
// NOT WORKING
var Job = Parse.Object.extend("Jobs");
var job = new Job();

job.set('title', 'test');
job.save({
    useMasterKey: true
}).then(res => {
    console.log("Works"); 
}).catch(error => {
   console.log(error); 
});

We're talking about saving a Parse.File not a Parse.Object

Parse server 2.2.4 with Dashboard 1.0.19 will not save files from the Browser either.

Output form chrome console:

POST https://api.xx.com/parse/files/test.png 403 (Forbidden) dashboard.bundle.js:9703
XHR finished loading: POST "https://api.xx.com/parse/files/test.png".

Could those be related?

Edit: we use all keys in the server config

@danibjor you mean 2.2.24? update to latest 2.2.25 please.

@flovilmart - yes, I'm performing it from CloudCode. I'm also setting everything in the constructor, and I'm 100% sure that the values match. See the constructor below. As for the client - I meant that I'm calling native client code from the Parse-iOS-SDK, not sending a request to CloudCode and handling the file there - and that works.

@danibjor - if its 2.2.24 or 2.2.25, it seems it could be related, as 403 indicates a permission error.

My ParseServer constructor:

var api = new ParseServer({
  databaseURI: process.env.MONGODB_URI,
  cloud: process.env.CLOUD_CODE_MAIN,
  appId: process.env.APP_ID,
  serverURL: process.env.SERVER_URL,
  masterKey: process.env.MASTER_KEY, 
  clientKey: process.env.CLIENT_KEY,
  restAPIKey: process.env.REST_API_KEY,
  javascriptKey: process.env.JAVASCRIPT_KEY,
  dotNetKey: process.env.DOT_NET_KEY,
  publicServerURL : process.env.SERVER_URL,
  filesAdapter: new S3Adapter(
    process.env.S3_ACCESS_KEY,
    process.env.S3_SECRET_KEY,
    process.env.S3_BUCKET,{
      directAccess: false,
      region: process.env.S3_BUCKET_REGION,
      bucketPrefix: process.env.S3_BUCKET_PREFIX
    }),
  push: {
    ios: [
      {
        pfx: './certificates/apns_dev.p12',
        passphrase: process.env.APNS_DEV_PASSWORD,
        bundleId: '***',
        production: false // Dev
      },
      {
        pfx: './certificates/apns_prod.p12',
        passphrase: process.env.APNS_PROD_PASSWORD,
        bundleId: '***',  
        production: true // Prod
      }
    ]
  }
});

what is the exact error you get when saving the file? I just ran some test locally (from the node SDK and latest version of parse-server) and I don't seem to be able to reproduce the issue.

I however managed to get unauthorized when my applicationId mismatched the server applicationID.

As you're on CloudCode, you should not need to call Parse.initialize. Is that called by anychance anywhere in your code with a _bad_ applicationId?

@flovilmart i've spent 3 hours on this issue today with many different setups trying to replicate this issue and i cant. I do get unauthorized but only on the steps ive taken above.

@MartinHerman i understand you say there is nothing in the logs, but yet you haven't posted any logs for us to work with. Sometimes it the smallest things that are overlooked.

@Cliffordwh what steps exactly? Also yes that's normal there's nothing in the log, I don't expect anything special in the logs as the request logger don't apply to the filesRouter (which is an issue in itself)

@flovilmart im with you!

@MartinHerman I might have a theory. Are you able to curla std cloud function like this?

Parse.Cloud.define('hello', function(req, res) {
  res.success('Hi');
});

@flovilmart @Cliffordwh - guys, if you're ever in Slovakia, beers are on me.

So it turns out, I was missing the javascriptKey in my constructor after all. I am declaring it as an environment variable. With Heroku, you set them up in the application settings and if you're running your code locally, you load them from an .env file. For safety purposes, it is advised to add the .env file to gitignore.

When working on #3051, I've added my javascriptKey both to Heroku and my local .env file. The thing is - I've been using my home computer and I'm at the office now. As the production code pulls the environment variables from Heroku, no one noticed the new environment variables missing in their local .env files until we tried locally testing some new server code.

Again guys, if you are ever around, beers are on me!

@MartinHerman i thought so! lol glad you got it working. All the best

Alight! Closing that! And enjoy the beers for us 馃嵒

@flovilmart tried upgrading the server to 2.2.25 and updated dashboard to latest bits.

Console still show 403 on uploading files.

Failed to load resource: the server responded with a status of 403 (Forbidden)
https://api.xx.com/parse/files/phone.jpg

application/octet-stream
XHR
https://api.xx.xom/parse/files/phone.jpg
https
api.xx.com
/parse/files/phone.jpg
phone.jpg

Metode  POST
Buffered    No
Status  Forbidden
Kode    403

The class has CLP public read/write

As @MartinHerman, make sure your keys are correctly set. If properly configured, it works correctly.

Had a similar issue. In your ParseServer constructor, try removing the javascriptKey.

either remove the javascriptKey from parse-server constructor, or provide it in the dashboard.

I also have this issue. I tried with and without the javascriptKey.
I allways get "unauthorized" while signing up a new user or even with the hello function from @Cliffordwh above.
I tried all versions above 2.2.24.

@Hitabis - could you post all fields of your Curl request and code from your ParseServer constructor?

@MartinHerman the constructor is taken from the docker image https://github.com/yongjhih/docker-parse-server/blob/master/index.js

var api = new ParseServer({
    databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
    databaseOptions: databaseOptions,
    cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',

    appId: process.env.APP_ID || 'myAppId',
    masterKey: process.env.MASTER_KEY, //Add your master key here. Keep it secret!
    serverURL: serverURL,

    collectionPrefix: process.env.COLLECTION_PREFIX,
    clientKey: process.env.CLIENT_KEY,
    restAPIKey: process.env.REST_API_KEY,
    javascriptKey: process.env.JAVASCRIPT_KEY,
    dotNetKey: process.env.DOTNET_KEY,
    fileKey: process.env.FILE_KEY,
    filesAdapter: filesAdapter,

    facebookAppIds: facebookAppIds,
    maxUploadSize: process.env.MAX_UPLOAD_SIZE,
    push: pushConfig,
    verifyUserEmails: verifyUserEmails,
    emailAdapter: emailAdapter,
    enableAnonymousUsers: enableAnonymousUsers,
    allowClientClassCreation: allowClientClassCreation,
    //oauth = {},
    appName: process.env.APP_NAME,
    publicServerURL: process.env.PUBLIC_SERVER_URL,
    liveQuery: liveQueryParam
    //customPages: process.env.CUSTOM_PAGES || // {
    //invalidLink: undefined,
    //verifyEmailSuccess: undefined,
    //choosePassword: undefined,
    //passwordResetSuccess: undefined
    //}
});

I set APP_ID,APP_NAME,MASTER_KEY,JAVASCRIPT_KEY,REST_API_KEY and some others from outside.

I took the example function above:

Parse.Cloud.define('hello', function(req, res) {
  res.success('Hi');
});

and the curl request is:

curl -X POST \
-H "X-Parse-Application-Id: schneewittchen" \
-H "X-Parse-REST-API-Key: undefined" \
-H "X-Parse-Session-Token: r:b1c78b6c39759bf4da0097c3d23d94af" \
http://localhost:1337/parse/functions/hello

the resonse on 2.2.21 is

{
    "result": "Hi"
}

the resonse on 2.2.24 is

{
    "error": "unauthorized"
}

comment out "javascriptKey: process.env.JAVASCRIPT_KEY"

@FransGH same situation. Can only access with master key.

You have specified at least one key in your server config. This means you must supply at least one key in any client call.

For your curl example you are passing a REST key with the string value undefined. If this does not match the REST key you provided when starting the server then authentication will fail. Session Tokens are not keys.

If you want to use request authentication then you must provide a valid key with every request.

If you don't want to use request authentication then remove all 4 client keys from your server config. This is not really any less secure and was the default before 2.24 if you didn't specify all keys.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kilabyte picture kilabyte  路  4Comments

mohmagdy picture mohmagdy  路  3Comments

ugo-geronimo picture ugo-geronimo  路  3Comments

ShawnBaek picture ShawnBaek  路  4Comments

lorki picture lorki  路  3Comments