Can't figure out how to upload Attachments using node-asana

This works fine for me:

// Create a client, getting parameters from the environment.
var client = asana.Client.create({
    clientId: process.env['client_id'],
    clientSecret: process.env['client_secret']
});

client.useOauth();

client.authorize().then(function() {
    // The client is authorized! Make a simple request.
    return client.users.me().then(function(me) {
        console.log('Hello ' + me.name);

        var params = {
            method: 'POST',
            url: "https://app.asana.com/api/1.0/tasks/1234567890/attachments",
            formData: {
                file: fs.createReadStream('test.png')
            },
            headers: {
                "Content-Type": "multipart/form-data"
            },
        };
        client.dispatcher.dispatch(params, {});
    });
}).catch(function(err) {
    console.log('An error occurred', err);
});

Your solution doesn’t work for me…it is asking to open a link in the browser and enter a code in the command line. The link is broken and I can’t figure out what code it wants as input.
In your code I can’t see how you authenticate a specific user with his oauth-token.

I use following code to send the request that is failing:

const {
  taskGid,
  refresh_token,
  clientId,
  clientSecret,
  redirectUri,
} = process.env;

console.log("authenticating client");
const client = asana.Client.create({
   clientId,
   clientSecret,
   redirectUri
});
client.useOauth({ credentials: { refresh_token } });

console.log("loading file from disk");
const file = fs.createReadStream("src/files/test.png");

console.log("starting fileupload");
client.dispatcher
  .dispatch({
    method: "POST",
    url: `https://app.asana.com/api/1.0/tasks/${taskGid}/attachments`,
    formData: { file },
    headers: {
      "Content-Type": "multipart/form-data"
    }
  })
  .then(response => {
    console.log("SUCCESS", response);
  })
  .catch(e => console.log("ERROR", e));

but this request works for me:

const options = {
  method: "POST",
  url: "https://app.asana.com/api/1.0/tasks/1234567890/attachments",
  formData: { file },
  headers: {
    "Content-Type": "multipart/form-data",
    Authorization:
      "Bearer XXXXXXXXXXXXXXXXXXXXXXXXX",
    "X-Asana-Client-Lib":
    "version=0.17.3&language=NodeJS&language_version=v8.17.0&os=win32"
  }
};

request(options, function(err, res, payload) {
  console.log(payload);
});

In our Authentication Basics docs we say "Native or command line applications should use the special redirect URL urn:ietf:wg:oauth:2.0:oob", which is what my script is doing. If you want it to work, go to your Developer Console in Asana and add urn:ietf:wg:oauth:2.0:oob as a redirect_url.

It appears you’re loading your refresh_token from an env_variable, which is only appropriate if your app runs for a single user. If it’s only ever going to be run for a single user, you should be using a PAT.

If this app is meant to be used for multiple people, then you should load a refresh token for each person on their first authentication.

Are you building a script that a local computer will run, a website, or a server?

I only isolated the attachment code to be sure it is not a problem of my application. In the end it should run on a nodeJS server for multiple users.
Using the client.authorize() method it always ask me for the credentials. Even i I pass them to the useOauth method. So I think your approach is not suited for my production application.

I am glad I finally have a working example with the pure request method that I can use. I just find it very confusing that I can’t make it work using your asana npm package. In the end it was causing more problems then it helped me.
I hope you and me can find the issue and your team can fix it so other people don’t have to waste hours and hours to find the problem.

Still not able to make it work either, that is getting very annoying :frowning:

I got it working by using the raw “request” method:

const options = {
	method: 'POST',
	url: `https://app.asana.com/api/1.0/tasks/${taskGid}/attachments`,
	formData: {
		file: fileStream,
	},
	headers: {
		'Content-Type': 'multipart/form-data',
		Authorization: `Bearer ${bearerToken}`,
	},
}

request(options)

it does not work with the asana-client and its dispatch method. Using an OAuth-Token the request will fail. If I’m using a personal-access-token, the asana-client will successfully upload my file.

I managed to make it work using the http service from Angular, after looking at a what a formData postman request looks like. It appears that the Asana dispatcher does not know how to handle form data request, cc @Ross_Grambo

It does when using an personal-access-token for authentication. The request will only fail using an OAuth authentication.