ocilo / skype-http

Unofficial Skype API for Node.js via HTTP
https://ocilo.github.io/skype-http
MIT License
51 stars 24 forks source link

Handle rate limits on registrationToken acquisition #55

Closed James91 closed 6 years ago

James91 commented 6 years ago

Hi, I get a Unable to register an endpoint error. Just wondering what triggers this?

Thanks, James

demurgos commented 6 years ago

Hi, thanks for your report. Do you have more details such as a stack trace? I just tried at the moment and I am able to use my own credentials. It means that it is not a global change in the API but likely related to your program or account. Did you create your account recently? Do you use 2 factor authentication? Anything special? Can you log into your account using the official web client?

Can you run the example? Up to which point?

git clone https://github.com/ocilo/skype-http.git
cd skype-http
npm install
npm start
James91 commented 6 years ago

I was just running multiple login attempts. So login repeat; after about 2-3 it would appear with that message.

demurgos commented 6 years ago

Ok, How did you do your login attempts: using your own code (with the API) or with the example program? If I remember well enough, the library detects correctly if the error is caused bu too many login attemps. Do you get an error? If so could you provide the stacktrace?

James91 commented 6 years ago
const skypeHttp = require("skype-http");

skypeHttp.connect({
    credentials: {
      username: "",
      password: ""
    },
    verbose: true
  })
  .then(function (api) {
    console.log(api);
  })
  .catch(function (error) {
    console.error(error);
  });

It's using your code. The only messages I get is unable to find the endpoint.

demurgos commented 6 years ago

Thanks, I found the line responsible for your error: https://github.com/ocilo/skype-http/blob/1e56b0e9a7b9fbbedae199aee1c1503251d8d628/src/lib/login.ts#L126

You should at least have the Acquired SkypeToken message. Skype first uses OAuth with the microsoft server to get a token authenticating your account: the Skype token (this works). Then it exchanges this Skype token for a "registration token" used to subscribe to resources. This is where things go wrong. It seems that your request receives a redirect instead of a response: there's actually a TODO in the code to handle this case. I'll fix it when I'll get back home in a few hours.

Since I am not able to reproduce this issue, I'll reach out to you to test my patch once it is done.

Thanks for reporting the issue and sorry for the inconvenience.

demurgos commented 6 years ago

Hi, I pushed a branch with improved error handling around endpoint registration: demurgos/skype-http#endpoint-registration. Could you check it out?

git clone https://github.com/demurgos/skype-http.git --branch endpoint-registration
cd skype-http
npm install
npm start

If you still get an error, it should be more detailed and contain the bad request/response. This error may contain some cookies. If you don't want to post the stack trace here, send me an email (see my profile).

James91 commented 6 years ago
EndpointRegistrationFailed: Unable to register endpoint
    at getRegistrationToken 
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
  caused by MissingHeader: Received response with headers `{ 'cache-control': 'no-store, must-revalidate, no-cache',
  pragma: 'no-cache',
  'content-length': '55',
  'content-type': 'application/json; charset=utf-8',
  statustext: 'Login Rate limit exceeded',
  'set-registrationtoken': '',
  contextid: '',
  date: 'Tue, 14 Nov 2017 12:58:32 GMT',
  connection: 'close' }` where the expected header 'Location' is missing. Request: { uri: 'https://db5-client-s.gateway.messenger.live.com/v1/users/ME/endpoints',
  headers: 
   { LockAndKey: '',
     ClientInfo: '',
     Authentication: '',
     BehaviorOverride: 'redirectAs404' },
  cookies: { idx: { 'login.skype.com': 
   { '/': 
      { 'login-vi':  } },
  'login.live.com': 
   { } },
  'live.com': 
   { '} },
  body: '{"endpointFeatures":"Agent"}' }, Response: { statusCode: 429,
  body: '{"errorCode":803,"message":"Login Rate limit exceeded"}',
  headers: 
   { 'cache-control': 'no-store, must-revalidate, no-cache',
     pragma: 'no-cache',
     'content-length': '55',
     'content-type': 'application/json; charset=utf-8',
     statustext: 'Login Rate limit exceeded',
     'set-registrationtoken': '',
     contextid: '',
     date: 'Tue, 14 Nov 2017 12:58:32 GMT',
     connection: 'close' } }
    at Object.create (/http.js:43:16)
    at getRegistrationToken (example/lib/login.js:122:49)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! skype-http@0.0.13 start: `node build/example/example/main.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the skype-http@0.0.13 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     2017-11-14T12_58_33_651Z-debug.log

The way I re-created the issue was by doing this in the example/main.ts code

while (true) {
      api = await skypeHttp.connect({credentials, verbose: true});
}
demurgos commented 6 years ago

Thanks. Did you have this error with npm start before starting your infinite loop?

In the stacktrace, we see the server's response:

{ statusCode: 429,
  body: '{"errorCode":803,"message":"Login Rate limit exceeded"}',
  headers: 
   { 'cache-control': 'no-store, must-revalidate, no-cache',
     pragma: 'no-cache',
     'content-length': '55',
     'content-type': 'application/json; charset=utf-8',
     statustext: 'Login Rate limit exceeded',
     'set-registrationtoken': '',
     contextid: '',
     date: 'Tue, 14 Nov 2017 12:58:32 GMT',
     connection: 'close' } }

As you can see, Skype responds with a rate limit error. With this log, I'll now be able to add a nicer error message for this specific case.

Still, you haven't replied to my main question: does the example program (npm start) succeeds? (Now that you had a rate limit error, you may need to wait a bit: check if you can connect to the web app before running the example program).

Is the error related to the rate limit or are there other reasons? Was your code already in an infinite loop in the first place?

James91 commented 6 years ago

Sorry - Didn't explain well.

npm start - That worked as expected, it logged in and started the message polling.

I then added the infinite loop, this was to test multiple login attempts. This then failed with the stack trace above. (After successfully logging in twice and then it failed)

demurgos commented 6 years ago

Ok, it means that the problem was only related to rate limits. I'll add a dedicated error for rate limits then. Would it be enough to solve your use case?

James91 commented 6 years ago

Yes that would be enough, means I can handle the issue on my end.

Thanks once again

Charles Samborski mailto:notifications@github.com 14 November 2017 at 14:57

Ok. I'll add a dedicated error for rate limits then. Would it be enough to solve your use case?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/ocilo/skype-http/issues/55#issuecomment-344284654, or mute the thread https://github.com/notifications/unsubscribe-auth/ABoxNYV9fmL68xVrhw3_YORNRYCQZ5zRks5s2aprgaJpZM4QZi0E.

demurgos commented 6 years ago

@James91 I submited a PR: https://github.com/ocilo/skype-http/pull/59

With this PR, you should be able to do something like:

try {
  const api = await login(...);
} catch(err) {
  switch(err.name) {
    case "EndpointRegistrationError":
      if (err.cause.name === "LoginRateLimitExceeded") {
        // Deal with rate limit
      }
      break;
    default:
      throw err;
  }
}