Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

About the userprofile interface #26

Open
Likenttt opened this issue Jan 3, 2022 · 10 comments
Open

About the userprofile interface #26

Likenttt opened this issue Jan 3, 2022 · 10 comments

Comments

@Likenttt
Copy link

Likenttt commented Jan 3, 2022

Hi, thanks for your masterpiece.
I found on connect.garmin.com the userprofile interface is like https://connect.garmin.com/modern/proxy/userprofile-service/userprofile/personal-information/userhash rather than that in this project /currentuser-service/user/info. Could you tell me why?
image

Looking forward for your reply

@Pythe1337N
Copy link
Owner

Hi, thanks!
The /currentuser-service/ is providing the userhash during login and is then stored within the client itself for future use. The /personal-information/endpoint seems to be providing personal information only and not the user hash that the client is looking for.
The reason that your code is failing to run might be that you've made too many requests and/or login attempts. Garmin seems to have added some very strict rules for their users lately. Wait a bit, try to run it again and let me know if it still doesn't work.

@Likenttt
Copy link
Author

Likenttt commented Jan 5, 2022

Hi, thanks! The /currentuser-service/ is providing the userhash during login and is then stored within the client itself for future use. The /personal-information/endpoint seems to be providing personal information only and not the user hash that the client is looking for. The reason that your code is failing to run might be that you've made too many requests and/or login attempts. Garmin seems to have added some very strict rules for their users lately. Wait a bit, try to run it again and let me know if it still doesn't work.

Thanks for your reply. Recently, I was making an application for desktop aiming to sync my activity data between global server and that in China mainland. Your code is clean and elegant. I would contribute some features later , is it ok

@Likenttt
Copy link
Author

Likenttt commented Jan 5, 2022

I will always get an error after running it in my test file.

Unhandled rejection StatusCodeError: 500 - "<!DOCTYPE html>\n<html\n\t\txmlns=\"http://www.w3.org/1999/xhtml\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>Garmin</title>\n\t\t<meta name=\"description\" content=\"\" />\n\t\t<meta name=\"keywords\" content=\"\" />\n\t\t<meta name=\"format-detection\" content=\"telephone=no\" />\n\t\t\n\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"/sso/css/error/error.css?20170505\" />\n\t</head>\n\t<body>\n\t\t<div>\n\t\t\t<h2>500 Server error</h2>\n\t\t</div>\n\t</body>\n</html>\n\n"
    at new StatusCodeError (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/request-promise-core/lib/errors.js:32:15)
    at Request.plumbing.callback (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/request-promise-core/lib/plumbing.js:104:33)
    at Request.RP$callback [as _callback] (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at self.callback (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/request/request.js:185:22)
    at onRequestComplete (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/cloudscraper/index.js:629:3)
    at onCloudflareResponse (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/cloudscraper/index.js:251:3)
    at onRequestResponse (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/cloudscraper/index.js:205:5)
    at Request.<anonymous> (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/cloudscraper/index.js:149:7)
    at Object.onceWrapper (events.js:520:26)
    at Request.emit (events.js:400:28)
    at Request.<anonymous> (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/request/request.js:1154:10)
    at Request.emit (events.js:400:28)
    at IncomingMessage.<anonymous> (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/request/request.js:1076:12)
    at Object.onceWrapper (events.js:519:28)
    at IncomingMessage.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1334:12)
    at processTicksAndRejections (internal/process/task_queues.js:82:21)

(node:31668) UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property 'body' of '(intermediate value)' as it is undefined.
    at CFClient.get (/Users/xxxxxx/WebstormProjects/garmin-connect/src/common/CFClient.js:46:17)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async GarminConnect.login (/Users/xxxxxx/WebstormProjects/garmin-connect/src/garmin/GarminConnect.js:60:33)
    at async main (/Users/xxxxxx/WebstormProjects/garmin-connect/src/lab.js:10:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:31668) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:31668) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Unhandled rejection RequestError: Error: write EPROTO 4376102272:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../deps/openssl/openssl/ssl/record/rec_layer_s3.c:1544:SSL alert number 40

    at onRequestResponse (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/cloudscraper/index.js:165:21)
    at Request.<anonymous> (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/cloudscraper/index.js:144:7)
    at Object.onceWrapper (events.js:520:26)
    at Request.emit (events.js:400:28)
    at Request.onRequestError (/Users/xxxxxx/WebstormProjects/garmin-connect/node_modules/request/request.js:877:8)
    at ClientRequest.emit (events.js:400:28)
    at TLSSocket.socketErrorListener (_http_client.js:475:9)
    at TLSSocket.emit (events.js:400:28)
    at emitErrorNT (internal/streams/destroy.js:106:8)
    at emitErrorCloseNT (internal/streams/destroy.js:74:3)
    at processTicksAndRejections (internal/process/task_queues.js:82:21)

@Likenttt
Copy link
Author

Likenttt commented Jan 5, 2022

Maybe this site is not used any more
image
Are u using this project currently?

@Pythe1337N
Copy link
Owner

I am using this library on a daily basis, and it seems to work for me. Interesting.
Are you a newly registered user? Just thinking that there exists an 'old backend' that did handle (and still does) 'old users' but new users are required to go through another endpoint. You're clearly getting a 500, which should indicate that something is up on Garmin's side. Usually you'll end up with 403 or some garbage plain text/html if authentication fails.

You can record a network log of all requests in a browser when you're signing in to your account and then backtrack and compare those to whats happening within the library.

@Pythe1337N
Copy link
Owner

Also found this
axios/axios#658
That might be related to the issues you're having.

@Likenttt
Copy link
Author

Likenttt commented Jan 7, 2022

I am using this library on a daily basis, and it seems to work for me. Interesting. Are you a newly registered user? Just thinking that there exists an 'old backend' that did handle (and still does) 'old users' but new users are required to go through another endpoint. You're clearly getting a 500, which should indicate that something is up on Garmin's side. Usually you'll end up with 403 or some garbage plain text/html if authentication fails.

You can record a network log of all requests in a browser when you're signing in to your account and then backtrack and compare those to whats happening within the library.

Thanks for your reply. I forked your project and rewrote the login method with the procedure from another repo garminexport. Thanks for your work.

@Likenttt
Copy link
Author

Likenttt commented Jan 7, 2022

Are you a newly registered user?

I have two accounts. One is under garmin.cn, registered in April,2019. And the other is under garmin.com, registered in Dec,2021. For Chinese mainland domain, I replaced all garmin.com with garmin.cn when access GC_CONNECT_MODERN etc. I don't know where was wrong.

@Likenttt
Copy link
Author

Likenttt commented Jan 7, 2022

I have tried the repo garminexport, and it worked for me.
I found the procedure was a little different from that you used.
That repo's login authentication will do like below.

  1. Get sso.garmin.com/sso/signin as a html text
  2. Use a regular expression to search the csrf token from text in 1 and add the csrf token as _csrf kv in data posted later
  3. Post and retrieve the response as html
  4. Use a regular expression to search the auth_ticket from 3
  5. Get the auth_ticket url(claim the ticket)
  6. Get connect.garmin.com/modern to finish the login process.

@Likenttt
Copy link
Author

Likenttt commented Jan 7, 2022

image
Hi, I have rewritten some scrapper to axios. But I always got 403 Forbidden and 1020 Cloudfare. After searching , I found some guys who used python ecountered the same issue. Someone say don't use tls1.3. But how can I ? If you have any idea , please tell me. Thanks. The code resides here https://github.com/Likenttt/garmin-connect

/Users/kent/.nvm/versions/node/v14.18.2/bin/node /Users/kent/WebstormProjects/garmin-connect/src/example.js
Error: Request failed with status code 403
    at createError (/Users/kent/WebstormProjects/garmin-connect/node_modules/axios/lib/core/createError.js:16:15)
    at settle (/Users/kent/WebstormProjects/garmin-connect/node_modules/axios/lib/core/settle.js:17:12)
    at IncomingMessage.handleStreamEnd (/Users/kent/WebstormProjects/garmin-connect/node_modules/axios/lib/adapters/http.js:293:11)
    at IncomingMessage.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1334:12)
    at processTicksAndRejections (internal/process/task_queues.js:82:21) {
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    validateStatus: [Function: validateStatus],
    headers: {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': 'application/json',
      'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',
      origin: 'https://sso.garmin.com',
      nk: 'NT',
      Cookie: [RequestJar],
      'Content-Length': 200
    },
    params: {
      service: 'https://connect.garmin.com/modern',
      gauthHost: 'https://sso.garmin.com/sso'
    },
    method: 'post',
    url: 'https://sso.garmin.com/sso/signin',
    data: '{"username":"[email protected]","password":"xxxxx","embed":"false","_csrf":"9E0C98E516A1346DB7239FF382C3028824D5B6CC1B476EF268E2464AC5DC8947DCD73491E3E05C4A3FC5F282319B07AD2B90","rememberme":"on"}'
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      prefinish: [Function: requestOnPrefinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    _contentLength: null,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: 'sso.garmin.com',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 10,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'sso.garmin.com',
      _readableState: [ReadableState],
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [Circular *1],
      [Symbol(res)]: [TLSWrap],
      [Symbol(verified)]: true,
      [Symbol(pendingSession)]: null,
      [Symbol(async_id_symbol)]: 75,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(kSetNoDelay)]: false,
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object],
      [Symbol(RequestTimeout)]: undefined
    },
    _header: 'POST /sso/signin?service=https:%2F%2Fconnect.garmin.com%2Fmodern&gauthHost=https:%2F%2Fsso.garmin.com%2Fsso HTTP/1.1\r\n' +
      'Accept: application/json, text/plain, */*\r\n' +
      'Content-Type: application/json\r\n' +
      'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36\r\n' +
      'origin: https://sso.garmin.com\r\n' +
      'nk: NT\r\n' +
      'Cookie: [object Object]\r\n' +
      'Content-Length: 200\r\n' +
      'Host: sso.garmin.com\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: noopPendingOutput],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object],
      requests: {},
      sockets: [Object],
      freeSockets: {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    path: '/sso/signin?service=https:%2F%2Fconnect.garmin.com%2Fmodern&gauthHost=https:%2F%2Fsso.garmin.com%2Fsso',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      socket: [TLSSocket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      headers: [Object],
      rawHeaders: [Array],
      trailers: {},
      rawTrailers: [],
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 403,
      statusMessage: 'Forbidden',
      client: [TLSSocket],
      _consuming: false,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'https://sso.garmin.com/sso/signin?service=https:%2F%2Fconnect.garmin.com%2Fmodern&gauthHost=https:%2F%2Fsso.garmin.com%2Fsso',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(RequestTimeout)]: undefined
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: 'sso.garmin.com',
    protocol: 'https:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 200,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'https://sso.garmin.com/sso/signin?service=https:%2F%2Fconnect.garmin.com%2Fmodern&gauthHost=https:%2F%2Fsso.garmin.com%2Fsso',
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      'user-agent': [Array],
      origin: [Array],
      nk: [Array],
      cookie: [Array],
      'content-length': [Array],
      host: [Array]
    }
  },
  response: {
    status: 403,
    statusText: 'Forbidden',
    headers: {
      date: 'Fri, 07 Jan 2022 16:00:17 GMT',
      'content-type': 'text/plain; charset=UTF-8',
      'content-length': '16',
      connection: 'close',
      'x-frame-options': 'SAMEORIGIN',
      'referrer-policy': 'same-origin',
      'cache-control': 'private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0',
      expires: 'Thu, 01 Jan 1970 00:00:01 GMT',
      'expect-ct': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"',
      'report-to': '{"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=1i3E%2FkD9M6vCSNS2HwIQdFxF%2BFr%2BuUKAMcBfnvMP8PmaXrrV6ZoT1QfBdGKPSgZ46ZSEVAuMJKkYHBxoX4MK7EHkQTl2eHpCCvqjv3eWyGQJPSQPG7LHrpKzIpv0xbAS"}],"group":"cf-nel","max_age":604800}',
      nel: '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}',
      server: 'cloudflare',
      'cf-ray': '6c9e65f03f266453-SJC'
    },
    config: {
      transitional: [Object],
      adapter: [Function: httpAdapter],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      validateStatus: [Function: validateStatus],
      headers: [Object],
      params: [Object],
      method: 'post',
      url: 'https://sso.garmin.com/sso/signin',
      data: '{"username":"[email protected]","password":"xxxxxx","embed":"false","_csrf":"9E0C98C16A1346DB7239FF382C3028824D5B6CC1B476EF268E2464AC5DC8947DCD73491E3E05C4A3FC5F282319B07AD2B90","rememberme":"on"}'
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      socket: [TLSSocket],
      _header: 'POST /sso/signin?service=https:%2F%2Fconnect.garmin.com%2Fmodern&gauthHost=https:%2F%2Fsso.garmin.com%2Fsso HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'Content-Type: application/json\r\n' +
        'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36\r\n' +
        'origin: https://sso.garmin.com\r\n' +
        'nk: NT\r\n' +
        'Cookie: [object Object]\r\n' +
        'Content-Length: 200\r\n' +
        'Host: sso.garmin.com\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: noopPendingOutput],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/sso/signin?service=https:%2F%2Fconnect.garmin.com%2Fmodern&gauthHost=https:%2F%2Fsso.garmin.com%2Fsso',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'sso.garmin.com',
      protocol: 'https:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    data: 'error code: 1020'
  },
  isAxiosError: true,
  toJSON: [Function: toJSON]
}
(node:84480) UnhandledPromiseRejectionWarning: Error: Auth failure: failed to post https://sso.garmin.com/sso/signin
    at /Users/kent/WebstormProjects/garmin-connect/src/common/CFClient.js:121:23
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async CFClient.postJson (/Users/kent/WebstormProjects/garmin-connect/src/common/CFClient.js:115:13)
    at async GarminConnect.login (/Users/kent/WebstormProjects/garmin-connect/src/garmin/GarminConnect.js:90:26)
    at async main (/Users/kent/WebstormProjects/garmin-connect/src/example.js:9:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:84480) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:84480) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Process finished with exit code 0


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants