node-asana icon indicating copy to clipboard operation
node-asana copied to clipboard

No proper way to catch a call after bearer token is expired?

Open ffxsam opened this issue 8 years ago • 9 comments

When I try to perform an operation when the bearer token is expired, I get this message on the server:

I20170223-11:15:03.228(-8)? * * * * * * * * * * * * * * * * * * * * * * * * * * *
I20170223-11:15:03.231(-8)? Please open a browser to the url:
I20170223-11:15:03.231(-8)?
I20170223-11:15:03.231(-8)? https://app.asana.com/-/oauth_authorize?client_id=xx&response_type=code&redirect_uri=xx&scope=default
I20170223-11:15:03.232(-8)?
I20170223-11:15:03.232(-8)? and follow the prompts to authorize this application.
I20170223-11:15:03.232(-8)? * * * * * * * * * * * * * * * * * * * * * * * * * * *

Despite there being console.log statements afterwards, they're never reached. The process flow is exited, no errors are thrown that I can catch. From a programmatic standpoint, this isn't very useful, as I'm not able to properly handle this exception.

ffxsam avatar Feb 23 '17 19:02 ffxsam

Here's what I would expect as a typical flow:

    try {
      client.useOauth({ credentials: token });
      result = await client.workspaces.findAll();
    } catch (e) {
      console.log('Error occurred, possibly using an expired token');
    }

ffxsam avatar Feb 23 '17 19:02 ffxsam

Also I hope that, since this is waiting for input, this isn't leaving extra processes hanging around on the server. That would be bad.

ffxsam avatar Feb 23 '17 20:02 ffxsam

➤ Drew Haven commented: Does the code support await? I've always used it with promises. In that case the exception and message may be occurring in another thread and you'll have to catch the exceptional state of the promise and not just follow on the success case.

Does that make sense? Does that apply in this situation?

Asana-Github avatar Feb 24 '17 19:02 Asana-Github

Using promises or await makes no difference. The package still acts as if I'm using it like a CLI, and prints a message to the console, asking for user input. Here's some test code I put together:

import Asana from 'asana';

function createAsanaClient() {
  return Asana.Client.create({
    clientId: 'xx',
    clientSecret: 'xx',
    redirectUri: 'https://xx',
  });
}

async function doThings() {
  const client = createAsanaClient();

  try {
    client.useOauth({ credentials: 'bleep' });
    const result = await client.projects.findByWorkspace(xx);
  } catch (e) {
    console.error('somethign bad happened');
  }
}

doThings().then(result => {
  console.log('result:', result);
}).catch(e => {
  console.error('err', e);
});

Doesn't matter what I do, client.projects.findByWorkspace (or any other method call) never really throws any kind of error that can be caught with try/catch. It just prints to the console, which again, has no point because nobody would ever see that.

ffxsam avatar Feb 24 '17 19:02 ffxsam

The comments for the useOauth method indicate that you can specify the oauth "flow type" that should be used in order to obtain credentials in the event the server returns a 401. The default behavior if no flow is specified is to autodetect, and for code running on the server this will run the NativeFlow which you are experiencing. You should be able to pass flowType: null and then, since the oauth component will not have any flow to run to get credentials, it should throw the error as you desire if the token it uses is expired.

slobak avatar Feb 24 '17 22:02 slobak

@slobak Ahh, gotcha. Thanks, I'll give that a shot!

It seems like there would be room for one more check in this function: https://github.com/Asana/node-asana/blob/8a99170f142127e7ddc2bd9ed1bb98f4a823b27c/lib/auth/auto_detect.js#L14-L34

which would somehow see that process.env exists, but also test to see if we're running as a web server rather than a CLI node process. Maybe checking process.env.ROOT_URL could work, but I suppose that depends on the platform used.

ffxsam avatar Feb 24 '17 23:02 ffxsam

@ffxsam did passing flowType: null to useOauth work for you? The only way I managed to "catch" exceptions was by checking for timeouts like client.users.me().timeout(5000).catch(function(){console.log("CATCH PROMISE")}). I happen not to care about the type of exception but that can't possibly be the intended way of catching.

MoorsTech avatar Oct 05 '17 14:10 MoorsTech

Sorry I can't be of more help! This was quite awhile back, and I'm no longer working on that project.

ffxsam avatar Oct 05 '17 15:10 ffxsam

I encountered the same problem yesterday.
In case someone stumble upon this, the workaround I found was to provide a prompt in options. This prompt is a function that simply throws an error:

try {
     client.useOauth({ 
         credentials: 'bleep',
         prompt: () => { throw new Error("I'm here because a prompt was requested") }
    });
     const result = await client.me();
}
catch (e) {
    console.error('something bad happened');
}

vdechef avatar Jun 13 '22 13:06 vdechef