alexa-cookie2 4.2.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,13 +16,19 @@ If the automatic authentication fails (which is more common case in the meantime
16
16
 
17
17
  Starting with version 2.0 of this library the proxy approach was changed to be more "as the Amazon mobile Apps" which registers a device at Amazon and uses OAuth tokens to handle the automatic refresh of the cookies afterwards. This should work seamless. A cookie is valid for 14 days, so it is preferred to refresh the cookie after 5-13 days (please report if it should be shorter).
18
18
 
19
+ ## Troubleshooting for getting the cookie and tokens initially
20
+ If you still use the E-Mail or SMS based 2FA flow then this might not work. Please update the 2FA/OTP method in the amazon settings to the current process.
21
+
22
+ If you open the Proxy URL from a mobile device where also the Alexa App is installed on it might be that it do not work because Amazon might open the Alexa App. So please use a device or PC where the Alexa App is not installed
23
+
24
+ If you see a page that tells you that "alexa.amazon.xx is deprecated" and you should use the alexa app and with a QR code on it when you enter the Proxy URL" then this means that you call the proxy URL with a different IP/Domainname then the one you entered in the "proxy own IP" settings or you adjusted the IP shown in the Adapter configuration. The "proxy own IP" setting **needs to** match the IP/Domainname you use to call the proxy URL!
25
+
19
26
  ## Example:
20
27
  See example folder!
21
28
 
22
29
  * **example.js** shows how to use the library to initially get a cookie
23
30
  * **refresh.js** shown how to use the library to refresh the cookies
24
31
 
25
-
26
32
  ## Usage
27
33
  Special note for callback return for parameter result:
28
34
 
@@ -49,6 +55,14 @@ Partly based on [Amazon Alexa Remote Control](http://blog.loetzimmer.de/2017/10/
49
55
  Thank you for that work.
50
56
 
51
57
  ## Changelog:
58
+ ### 5.0.1 (2023-11-24)
59
+ * (adn77) make registered device name configurable by Appname
60
+ * (Apollon77) Prevent some error/crash cases
61
+
62
+ ### 5.0.0 (2023-09-08)
63
+ * IMPORTANT: Node.js 16 is now required minimum Node.js version!
64
+ * (Apollon77) Enhance registration process by also registering the app capabilities to allow usage of new HTTP/2 push connection
65
+
52
66
  ### 4.2.0 (2023-08-08)
53
67
  * (Hive) Adds the ability to alter the close proxy message
54
68
 
package/alexa-cookie.js CHANGED
@@ -19,8 +19,8 @@ const defaultUserAgentLinux = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.3
19
19
  const defaultProxyCloseWindowHTML = '<b>Amazon Alexa Cookie successfully retrieved. You can close the browser.</b>';
20
20
  const defaultAcceptLanguage = 'de-DE';
21
21
 
22
- const apiCallVersion = '2.2.485407.0';
23
- const apiCallUserAgent = 'AmazonWebView/Amazon Alexa/2.2.485407.0/iOS/15.5/iPhone';
22
+ const apiCallVersion = '2.2.556530.0';
23
+ const apiCallUserAgent = 'AmazonWebView/Amazon Alexa/2.2.556530.0/iOS/16.6/iPhone';
24
24
  const defaultAppName = 'ioBroker Alexa2';
25
25
 
26
26
  const csrfOptions = [
@@ -182,7 +182,7 @@ function AlexaCookie() {
182
182
  _options.logger && _options.logger(`Alexa-Cookie: Use as Accept-Language: ${_options.acceptLanguage}`);
183
183
 
184
184
  _options.proxyCloseWindowHTML = _options.proxyCloseWindowHTML || defaultProxyCloseWindowHTML;
185
-
185
+
186
186
  if (_options.setupProxy && !_options.proxyOwnIp) {
187
187
  _options.logger && _options.logger('Alexa-Cookie: Own-IP Setting missing for Proxy. Disabling!');
188
188
  _options.setupProxy = false;
@@ -386,7 +386,7 @@ function AlexaCookie() {
386
386
  if (!_options.proxyPort || _options.proxyPort === 0) {
387
387
  _options.proxyPort = proxyServer.address().port;
388
388
  }
389
- const errMessage = `You can try to get the cookie manually by opening http://${_options.proxyOwnIp}:${_options.proxyPort}/ with your browser.`;
389
+ const errMessage = `Please open http://${_options.proxyOwnIp}:${_options.proxyPort}/ with your browser and login to Amazon. The cookie will be output here after successfull login.`;
390
390
  callback && callback(new Error(errMessage), null);
391
391
  });
392
392
  }
@@ -435,20 +435,15 @@ function AlexaCookie() {
435
435
  'customer_info'
436
436
  ],
437
437
  'cookies': {
438
- 'website_cookies': [
439
- /*{
440
- "Value": cookies["session-id-time"],
441
- "Name": "session-id-time"
442
- }*/
443
- ],
438
+ 'website_cookies': [],
444
439
  'domain': `.${_options.baseAmazonPage}`
445
440
  },
446
441
  'registration_data': {
447
442
  'domain': 'Device',
448
443
  'app_version': apiCallVersion,
449
444
  'device_type': 'A2IVLV5VM2W81',
450
- 'device_name': '%FIRST_NAME%\u0027s%DUPE_STRATEGY_1ST%ioBroker Alexa2',
451
- 'os_version': '15.5',
445
+ 'device_name': '%FIRST_NAME%\u0027s%DUPE_STRATEGY_1ST%' + _options.deviceAppName,
446
+ 'os_version': '16.6',
452
447
  'device_serial': deviceSerial,
453
448
  'device_model': 'iPhone',
454
449
  'app_name': _options.deviceAppName,
@@ -524,6 +519,7 @@ function AlexaCookie() {
524
519
  }
525
520
  Cookie = addCookies(Cookie, response.headers);
526
521
  loginData.refreshToken = body.response.success.tokens.bearer.refresh_token;
522
+ const accessToken = body.response.success.tokens.bearer.access_token;
527
523
  loginData.tokenDate = Date.now();
528
524
  loginData.macDms = body.response.success.tokens.mac_dms;
529
525
 
@@ -535,78 +531,110 @@ function AlexaCookie() {
535
531
  Cookie = addCookies(Cookie, {'set-cookie': newCookies});
536
532
  }
537
533
 
538
- /*
539
- Get Amazon Marketplace Country
540
- */
534
+ registerTokenCapabilities(accessToken, () => {
535
+ /*
536
+ Get Amazon Marketplace Country
537
+ */
541
538
 
542
- const options = {
543
- host: `alexa.${_options.baseAmazonPage}`,
544
- path: `/api/users/me?platform=ios&version=${apiCallVersion}`,
545
- method: 'GET',
546
- headers: {
547
- 'User-Agent': apiCallUserAgent,
548
- 'Accept-Language': _options.acceptLanguage,
549
- 'Accept-Charset': 'utf-8',
550
- 'Connection': 'keep-alive',
551
- 'Accept': 'application/json',
552
- 'Cookie': Cookie
553
- }
554
- };
555
- _options.logger && _options.logger('Alexa-Cookie: Get User data');
556
- _options.logger && _options.logger(JSON.stringify(options));
557
- request(options, (error, response, body) => {
558
- if (!error) {
559
- try {
560
- if (typeof body !== 'object') body = JSON.parse(body);
561
- } catch (err) {
562
- _options.logger && _options.logger(`Get User data Response: ${JSON.stringify(body)}`);
563
- callback && callback(err, null);
564
- return;
539
+ const options = {
540
+ host: `alexa.${_options.baseAmazonPage}`,
541
+ path: `/api/users/me?platform=ios&version=${apiCallVersion}`,
542
+ method: 'GET',
543
+ headers: {
544
+ 'User-Agent': apiCallUserAgent,
545
+ 'Accept-Language': _options.acceptLanguage,
546
+ 'Accept-Charset': 'utf-8',
547
+ 'Connection': 'keep-alive',
548
+ 'Accept': 'application/json',
549
+ 'Cookie': Cookie
565
550
  }
566
- _options.logger && _options.logger(`Get User data Response: ${JSON.stringify(body)}`);
551
+ };
552
+ _options.logger && _options.logger('Alexa-Cookie: Get User data');
553
+ _options.logger && _options.logger(JSON.stringify(options));
554
+ request(options, (error, response, body) => {
555
+ if (!error) {
556
+ try {
557
+ if (typeof body !== 'object') body = JSON.parse(body);
558
+ } catch (err) {
559
+ _options.logger && _options.logger(`Get User data Response: ${JSON.stringify(body)}`);
560
+ callback && callback(err, null);
561
+ return;
562
+ }
563
+ _options.logger && _options.logger(`Get User data Response: ${JSON.stringify(body)}`);
567
564
 
568
- Cookie = addCookies(Cookie, response.headers);
565
+ Cookie = addCookies(Cookie, response.headers);
569
566
 
570
- if (body.marketPlaceDomainName) {
571
- const pos = body.marketPlaceDomainName.indexOf('.');
572
- if (pos !== -1) _options.amazonPage = body.marketPlaceDomainName.substr(pos + 1);
567
+ if (body.marketPlaceDomainName) {
568
+ const pos = body.marketPlaceDomainName.indexOf('.');
569
+ if (pos !== -1) _options.amazonPage = body.marketPlaceDomainName.substr(pos + 1);
570
+ }
571
+ loginData.amazonPage = _options.amazonPage;
572
+ } else if (error && (!_options || !_options.amazonPage)) {
573
+ callback && callback(error, null);
574
+ return;
575
+ } else if (error && (!_options.formerRegistrationData || !_options.formerRegistrationData.amazonPage) && _options.amazonPage) {
576
+ _options.logger && _options.logger(`Continue with externally set amazonPage: ${_options.amazonPage}`);
577
+ } else if (error) {
578
+ _options.logger && _options.logger('Ignore error while getting user data and amazonPage because previously set amazonPage is available');
573
579
  }
574
- loginData.amazonPage = _options.amazonPage;
575
- } else if (error && (!_options || !_options.amazonPage)) {
576
- callback && callback(error, null);
577
- return;
578
- } else if (error && (!_options.formerRegistrationData || !_options.formerRegistrationData.amazonPage) && _options.amazonPage) {
579
- _options.logger && _options.logger(`Continue with externally set amazonPage: ${_options.amazonPage}`);
580
- } else if (error) {
581
- _options.logger && _options.logger('Ignore error while getting user data and amazonPage because previously set amazonPage is available');
582
- }
583
-
584
- loginData.loginCookie = Cookie;
585
580
 
586
- getLocalCookies(loginData.amazonPage, loginData.refreshToken, (err, localCookie) => {
587
- if (err) {
588
- callback && callback(err, null);
589
- }
581
+ loginData.loginCookie = Cookie;
590
582
 
591
- loginData.localCookie = localCookie;
592
- getCSRFFromCookies(loginData.localCookie, _options, (err, resData) => {
583
+ getLocalCookies(loginData.amazonPage, loginData.refreshToken, (err, localCookie) => {
593
584
  if (err) {
594
- callback && callback(new Error(`Error getting csrf for ${loginData.amazonPage}`), null);
595
- return;
585
+ callback && callback(err, null);
596
586
  }
597
- loginData.localCookie = resData.cookie;
598
- loginData.csrf = resData.csrf;
599
- delete loginData.accessToken;
600
- delete loginData.authorization_code;
601
- delete loginData.verifier;
602
- _options.logger && _options.logger(`Final Registration Result: ${JSON.stringify(loginData)}`);
603
- callback && callback(null, loginData);
587
+
588
+ loginData.localCookie = localCookie;
589
+ getCSRFFromCookies(loginData.localCookie, _options, (err, resData) => {
590
+ if (err) {
591
+ callback && callback(new Error(`Error getting csrf for ${loginData.amazonPage}`), null);
592
+ return;
593
+ }
594
+ loginData.localCookie = resData.cookie;
595
+ loginData.csrf = resData.csrf;
596
+ delete loginData.accessToken;
597
+ delete loginData.authorization_code;
598
+ delete loginData.verifier;
599
+ loginData.dataVersion = 2;
600
+ _options.logger && _options.logger(`Final Registration Result: ${JSON.stringify(loginData)}`);
601
+ callback && callback(null, loginData);
602
+ });
604
603
  });
605
604
  });
606
605
  });
607
606
  });
608
607
  };
609
608
 
609
+ const registerTokenCapabilities = (accessToken, callback) => {
610
+ /*
611
+ Register Capabilities - mainly needed for HTTP/2 push infos
612
+ */
613
+ const options = {
614
+ host: `api.amazonalexa.com`, // How Domains needs to be for other regions? au/jp?
615
+ path: `/v1/devices/@self/capabilities`,
616
+ method: 'PUT',
617
+ headers: {
618
+ 'User-Agent': apiCallUserAgent,
619
+ 'Accept-Language': _options.acceptLanguage,
620
+ 'Accept-Charset': 'utf-8',
621
+ 'Connection': 'keep-alive',
622
+ 'Content-type': 'application/json; charset=UTF-8',
623
+ 'authorization': `Bearer ${accessToken}`,
624
+ },
625
+ body: '{"legacyFlags":{"SUPPORTS_COMMS":true,"SUPPORTS_ARBITRATION":true,"SCREEN_WIDTH":1170,"SUPPORTS_SCRUBBING":true,"SPEECH_SYNTH_SUPPORTS_TTS_URLS":false,"SUPPORTS_HOME_AUTOMATION":true,"SUPPORTS_DROPIN_OUTBOUND":true,"FRIENDLY_NAME_TEMPLATE":"VOX","SUPPORTS_SIP_OUTBOUND_CALLING":true,"VOICE_PROFILE_SWITCHING_DISABLED":true,"SUPPORTS_LYRICS_IN_CARD":false,"SUPPORTS_DATAMART_NAMESPACE":"Vox","SUPPORTS_VIDEO_CALLING":true,"SUPPORTS_PFM_CHANGED":true,"SUPPORTS_TARGET_PLATFORM":"TABLET","SUPPORTS_SECURE_LOCKSCREEN":false,"AUDIO_PLAYER_SUPPORTS_TTS_URLS":false,"SUPPORTS_KEYS_IN_HEADER":false,"SUPPORTS_MIXING_BEHAVIOR_FOR_AUDIO_PLAYER":false,"AXON_SUPPORT":true,"SUPPORTS_TTS_SPEECHMARKS":true},"envelopeVersion":"20160207","capabilities":[{"version":"0.1","interface":"CardRenderer","type":"AlexaInterface"},{"interface":"Navigation","type":"AlexaInterface","version":"1.1"},{"type":"AlexaInterface","version":"2.0","interface":"Alexa.Comms.PhoneCallController"},{"type":"AlexaInterface","version":"1.1","interface":"ExternalMediaPlayer"},{"type":"AlexaInterface","interface":"Alerts","configurations":{"maximumAlerts":{"timers":2,"overall":99,"alarms":2}},"version":"1.3"},{"version":"1.0","interface":"Alexa.Display.Window","type":"AlexaInterface","configurations":{"templates":[{"type":"STANDARD","id":"app_window_template","configuration":{"sizes":[{"id":"fullscreen","type":"DISCRETE","value":{"value":{"height":1440,"width":3200},"unit":"PIXEL"}}],"interactionModes":["mobile_mode","auto_mode"]}}]}},{"type":"AlexaInterface","interface":"AccessoryKit","version":"0.1"},{"type":"AlexaInterface","interface":"Alexa.AudioSignal.ActiveNoiseControl","version":"1.0","configurations":{"ambientSoundProcessingModes":[{"name":"ACTIVE_NOISE_CONTROL"},{"name":"PASSTHROUGH"}]}},{"interface":"PlaybackController","type":"AlexaInterface","version":"1.0"},{"version":"1.0","interface":"Speaker","type":"AlexaInterface"},{"version":"1.0","interface":"SpeechSynthesizer","type":"AlexaInterface"},{"version":"1.0","interface":"AudioActivityTracker","type":"AlexaInterface"},{"type":"AlexaInterface","interface":"Alexa.Camera.LiveViewController","version":"1.0"},{"type":"AlexaInterface","version":"1.0","interface":"Alexa.Input.Text"},{"type":"AlexaInterface","interface":"Alexa.PlaybackStateReporter","version":"1.0"},{"version":"1.1","interface":"Geolocation","type":"AlexaInterface"},{"interface":"Alexa.Health.Fitness","version":"1.0","type":"AlexaInterface"},{"interface":"Settings","type":"AlexaInterface","version":"1.0"},{"configurations":{"interactionModes":[{"dialog":"SUPPORTED","interactionDistance":{"value":18,"unit":"INCHES"},"video":"SUPPORTED","keyboard":"SUPPORTED","id":"mobile_mode","uiMode":"MOBILE","touch":"SUPPORTED"},{"video":"UNSUPPORTED","dialog":"SUPPORTED","interactionDistance":{"value":36,"unit":"INCHES"},"uiMode":"AUTO","touch":"SUPPORTED","id":"auto_mode","keyboard":"UNSUPPORTED"}]},"type":"AlexaInterface","interface":"Alexa.InteractionMode","version":"1.0"},{"type":"AlexaInterface","configurations":{"catalogs":[{"type":"IOS_APP_STORE","identifierTypes":["URI_HTTP_SCHEME","URI_CUSTOM_SCHEME"]}]},"version":"0.2","interface":"Alexa.Launcher"},{"interface":"System","version":"1.0","type":"AlexaInterface"},{"interface":"Alexa.IOComponents","type":"AlexaInterface","version":"1.4"},{"type":"AlexaInterface","interface":"Alexa.FavoritesController","version":"1.0"},{"version":"1.0","type":"AlexaInterface","interface":"Alexa.Mobile.Push"},{"type":"AlexaInterface","interface":"InteractionModel","version":"1.1"},{"interface":"Alexa.PlaylistController","type":"AlexaInterface","version":"1.0"},{"interface":"SpeechRecognizer","type":"AlexaInterface","version":"2.1"},{"interface":"AudioPlayer","type":"AlexaInterface","version":"1.3"},{"type":"AlexaInterface","version":"3.1","interface":"Alexa.RTCSessionController"},{"interface":"VisualActivityTracker","version":"1.1","type":"AlexaInterface"},{"interface":"Alexa.PlaybackController","version":"1.0","type":"AlexaInterface"},{"type":"AlexaInterface","interface":"Alexa.SeekController","version":"1.0"},{"interface":"Alexa.Comms.MessagingController","type":"AlexaInterface","version":"1.0"}]}'
626
+ };
627
+ _options.logger && _options.logger('Alexa-Cookie: Register capabilities');
628
+ _options.logger && _options.logger(JSON.stringify(options));
629
+ request(options, (error, response, body) => {
630
+ if (error || (response.statusCode !== 204 && response.statusCode !== 200)) {
631
+ _options.logger && _options.logger('Alexa-Cookie: Could not set capabilities, Push connection might not work!');
632
+ _options.logger && _options.logger(`Alexa-Cookie: ${JSON.stringify(error)}: ${JSON.stringify(body)}`);
633
+ }
634
+ callback && callback();
635
+ });
636
+ };
637
+
610
638
  const getLocalCookies = (amazonPage, refreshToken, callback) => {
611
639
  Cookie = ''; // Reset because we are switching domains
612
640
  /*
@@ -621,10 +649,9 @@ function AlexaCookie() {
621
649
  'requested_token_type': 'auth_cookies',
622
650
  'source_token_type': 'refresh_token',
623
651
  'di.hw.version': 'iPhone',
624
- 'di.sdk.version': '6.12.3',
625
- 'cookies': Buffer.from(`{„cookies“:{".${amazonPage}":[]}}`).toString('base64'),
652
+ 'di.sdk.version': '6.12.4',
626
653
  'app_name': _options.deviceAppName || defaultAppName,
627
- 'di.os.version': '15.5'
654
+ 'di.os.version': '16.6'
628
655
  };
629
656
  const options = {
630
657
  host: `www.${amazonPage}`,
@@ -636,7 +663,9 @@ function AlexaCookie() {
636
663
  'Accept-Charset': 'utf-8',
637
664
  'Connection': 'keep-alive',
638
665
  'Content-Type': 'application/x-www-form-urlencoded',
639
- 'Accept': '*/*'
666
+ 'Accept': '*/*',
667
+ 'Cookie': Cookie,
668
+ 'x-amzn-identity-auth-domain': `api.${amazonPage}`
640
669
  },
641
670
  body: querystring.stringify(exchangeParams, null, null, {
642
671
  encodeURIComponent: encodeURIComponent
@@ -709,7 +738,7 @@ function AlexaCookie() {
709
738
  const refreshData = {
710
739
  'app_name': _options.deviceAppName || defaultAppName,
711
740
  'app_version': apiCallVersion,
712
- 'di.sdk.version': '6.12.3',
741
+ 'di.sdk.version': '6.12.4',
713
742
  'source_token': _options.formerRegistrationData.refreshToken,
714
743
  'package_name': 'com.amazon.echo',
715
744
  'di.hw.version': 'iPhone',
@@ -717,8 +746,8 @@ function AlexaCookie() {
717
746
  'requested_token_type': 'access_token',
718
747
  'source_token_type': 'refresh_token',
719
748
  'di.os.name': 'iOS',
720
- 'di.os.version': '15.5',
721
- 'current_version': '6.12.3'
749
+ 'di.os.version': '16.6',
750
+ 'current_version': '6.12.4'
722
751
  };
723
752
 
724
753
  const options = {
@@ -8,12 +8,17 @@ const alexaCookie = require('../alexa-cookie');
8
8
 
9
9
  const config = {
10
10
  logger: console.log,
11
- amazonPage: 'amazon.com', // optional: possible to use with different countries, default is 'amazon.de'
12
- acceptLanguage: 'en-US', // optional: webpage language, should match to amazon-Page, default is 'de-DE'
11
+ proxyOwnIp: '...', // required if proxy enabled: provide the own IP with which you later access the proxy.
12
+ // Providing/Using a hostname here can lead to issues!
13
+ // Needed to set up all rewriting and proxy stuff internally
14
+
15
+ // The following options are optional. Try without them first and just use really needed ones!!
16
+
17
+ amazonPage: 'amazon.de', // optional: possible to use with different countries, default is 'amazon.de'
18
+ acceptLanguage: 'de-DE', // optional: webpage language, should match to amazon-Page, default is 'de-DE'
13
19
  userAgent: '...', // optional: own userAgent to use for all request, overwrites default one, should not be needed
14
20
  proxyOnly: true, // optional: should only the proxy method be used? When no email/password are provided this will set to true automatically, default: false
15
21
  setupProxy: true, // optional: should the library setup a proxy to get cookie when automatic way did not worked? Default false!
16
- proxyOwnIp: '...', // required if proxy enabled: provide own IP or hostname to later access the proxy. needed to setup all rewriting and proxy stuff internally
17
22
  proxyPort: 3456, // optional: use this port for the proxy, default is 0 means random port is selected
18
23
  proxyListenBind: '0.0.0.0',// optional: set this to bind the proxy to a special IP, default is '0.0.0.0'
19
24
  proxyLogLevel: 'info', // optional: Loglevel of Proxy, default 'warn'
package/lib/proxy.js CHANGED
@@ -28,7 +28,7 @@ function addCookies(Cookie, headers) {
28
28
  }
29
29
  Cookie = '';
30
30
  for (const name of Object.keys(cookies)) {
31
- Cookie += name + '=' + cookies[name] + '; ';
31
+ Cookie += `${name}=${cookies[name]}; `;
32
32
  }
33
33
  Cookie = Cookie.replace(/[; ]*$/, '');
34
34
  return Cookie;
@@ -175,7 +175,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
175
175
 
176
176
  function router(req) {
177
177
  const url = (req.originalUrl || req.url);
178
- _options.logger && _options.logger('Router: ' + url + ' / ' + req.method + ' / ' + JSON.stringify(req.headers));
178
+ _options.logger && _options.logger(`Router: ${url} / ${req.method} / ${JSON.stringify(req.headers)}`);
179
179
  if (req.headers.host === `${_options.proxyOwnIp}:${_options.proxyPort}`) {
180
180
  if (url.startsWith(`/www.${_options.baseAmazonPage}/`)) {
181
181
  return `https://www.${_options.baseAmazonPage}`;
@@ -190,7 +190,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
190
190
  }
191
191
  if (url === '/') { // initial redirect
192
192
  returnedInitUrl = `https://www.${_options.baseAmazonPage}/ap/signin?openid.return_to=https%3A%2F%2Fwww.${_options.baseAmazonPage}%2Fap%2Fmaplanding&openid.assoc_handle=amzn_dp_project_dee_ios${_options.baseAmazonPageHandle}&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&pageId=amzn_dp_project_dee_ios${_options.baseAmazonPageHandle}&accountStatusPolicy=P1&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns.oa2=http%3A%2F%2Fwww.${_options.baseAmazonPage}%2Fap%2Fext%2Foauth%2F2&openid.oa2.client_id=device%3A${deviceId}&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&openid.oa2.response_type=code&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.pape.max_auth_age=0&openid.oa2.scope=device_auth_access&openid.oa2.code_challenge_method=S256&openid.oa2.code_challenge=${code_challenge}&language=${_options.amazonPageProxyLanguage}`;
193
- _options.logger && _options.logger('Alexa-Cookie: Initial Page Request: ' + returnedInitUrl);
193
+ _options.logger && _options.logger(`Alexa-Cookie: Initial Page Request: ${returnedInitUrl}`);
194
194
  return returnedInitUrl;
195
195
  }
196
196
  else {
@@ -201,12 +201,12 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
201
201
  }
202
202
 
203
203
  function onError(err, req, res) {
204
- _options.logger && _options.logger('ERROR: ' + err);
204
+ _options.logger && _options.logger(`ERROR: ${err}`);
205
205
  try {
206
206
  res.writeHead(500, {
207
207
  'Content-Type': 'text/plain'
208
208
  });
209
- res.end('Proxy-Error: ' + err);
209
+ res.end(`Proxy-Error: ${err}`);
210
210
  } catch (err) {
211
211
  // ignore
212
212
  }
@@ -239,18 +239,18 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
239
239
  if (url.endsWith('.ico') || url.endsWith('.js') || url.endsWith('.ttf') || url.endsWith('.svg') || url.endsWith('.png') || url.endsWith('.appcache')) return;
240
240
  //if (url.startsWith('/ap/uedata')) return;
241
241
 
242
- _options.logger && _options.logger('Alexa-Cookie: Proxy-Request: ' + req.method + ' ' + url);
242
+ _options.logger && _options.logger(`Alexa-Cookie: Proxy-Request: ${req.method} ${url}`);
243
243
  //_options.logger && _options.logger('Alexa-Cookie: Proxy-Request-Data: ' + customStringify(proxyReq, null, 2));
244
244
 
245
245
  if (typeof proxyReq.getHeader === 'function') {
246
- _options.logger && _options.logger('Alexa-Cookie: Headers: ' + JSON.stringify(proxyReq.getHeaders()));
246
+ _options.logger && _options.logger(`Alexa-Cookie: Headers: ${JSON.stringify(proxyReq.getHeaders())}`);
247
247
  let reqCookie = proxyReq.getHeader('cookie');
248
248
  if (reqCookie === undefined) {
249
249
  reqCookie = '';
250
250
  }
251
251
  for (const cookie of Object.keys(initialCookies)) {
252
- if (!reqCookie.includes(cookie + '=')) {
253
- reqCookie += '; ' + cookie + '=' + initialCookies[cookie];
252
+ if (!reqCookie.includes(`${cookie}=`)) {
253
+ reqCookie += `; ${cookie}=${initialCookies[cookie]}`;
254
254
  }
255
255
  }
256
256
  if (reqCookie.startsWith('; ')) {
@@ -260,9 +260,9 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
260
260
  if (!proxyCookies.length) {
261
261
  proxyCookies = reqCookie;
262
262
  } else {
263
- proxyCookies += '; ' + reqCookie;
263
+ proxyCookies += `; ${reqCookie}`;
264
264
  }
265
- _options.logger && _options.logger('Alexa-Cookie: Headers: ' + JSON.stringify(proxyReq.getHeaders()));
265
+ _options.logger && _options.logger(`Alexa-Cookie: Headers: ${JSON.stringify(proxyReq.getHeaders())}`);
266
266
  }
267
267
 
268
268
  let modified = false;
@@ -271,11 +271,11 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
271
271
  const fixedReferer = replaceHostsBack(proxyReq.getHeader('referer'));
272
272
  if (fixedReferer ) {
273
273
  proxyReq.setHeader('referer', fixedReferer);
274
- _options.logger && _options.logger('Alexa-Cookie: Modify headers: Changed Referer: ' + fixedReferer);
274
+ _options.logger && _options.logger(`Alexa-Cookie: Modify headers: Changed Referer: ${fixedReferer}`);
275
275
  modified = true;
276
276
  }
277
277
  }
278
- if (typeof proxyReq.getHeader === 'function' && proxyReq.getHeader('origin') !== 'https://' + proxyReq.getHeader('host')) {
278
+ if (typeof proxyReq.getHeader === 'function' && proxyReq.getHeader('origin') !== `https://${proxyReq.getHeader('host')}`) {
279
279
  proxyReq.setHeader('origin', `https://www.${_options.baseAmazonPage}`);
280
280
  _options.logger && _options.logger('Alexa-Cookie: Modify headers: Delete Origin');
281
281
  modified = true;
@@ -286,7 +286,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
286
286
  postBody += chunk.toString(); // convert Buffer to string
287
287
  });
288
288
  }
289
- _options.proxyLogLevel === 'debug' && _options.logger && _options.logger('Alexa-Cookie: Proxy-Request: (modified:' + modified + ')' + customStringify(proxyReq, null, 2));
289
+ _options.proxyLogLevel === 'debug' && _options.logger && _options.logger(`Alexa-Cookie: Proxy-Request: (modified:${modified})${customStringify(proxyReq, null, 2)}`);
290
290
  }
291
291
 
292
292
  function onProxyRes(proxyRes, req, res) {
@@ -296,9 +296,9 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
296
296
  //_options.logger && _options.logger('Proxy-Response: ' + customStringify(proxyRes, null, 2));
297
297
  let reqestHost = null;
298
298
  if (proxyRes.socket && proxyRes.socket._host) reqestHost = proxyRes.socket._host;
299
- _options.logger && _options.logger('Alexa-Cookie: Proxy Response from Host: ' + reqestHost);
300
- _options.proxyLogLevel === 'debug' && _options.logger && _options.logger('Alexa-Cookie: Proxy-Response Headers: ' + customStringify(proxyRes.headers, null, 2));
301
- _options.proxyLogLevel === 'debug' && _options.logger && _options.logger('Alexa-Cookie: Proxy-Response Outgoing: ' + customStringify(proxyRes.socket.parser.outgoing, null, 2));
299
+ _options.logger && _options.logger(`Alexa-Cookie: Proxy Response from Host: ${reqestHost}`);
300
+ _options.proxyLogLevel === 'debug' && _options.logger && _options.logger(`Alexa-Cookie: Proxy-Response Headers: ${customStringify(proxyRes.headers, null, 2)}`);
301
+ _options.proxyLogLevel === 'debug' && _options.logger && _options.logger(`Alexa-Cookie: Proxy-Response Outgoing: ${customStringify(proxyRes.socket.parser.outgoing, null, 2)}`);
302
302
  //_options.logger && _options.logger('Proxy-Response RES!!: ' + customStringify(res, null, 2));
303
303
 
304
304
  if (proxyRes && proxyRes.headers && proxyRes.headers['set-cookie']) {
@@ -308,7 +308,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
308
308
  }
309
309
  proxyCookies = addCookies(proxyCookies, proxyRes.headers);
310
310
  }
311
- _options.logger && _options.logger('Alexa-Cookie: Cookies handled: ' + JSON.stringify(proxyCookies));
311
+ _options.logger && _options.logger(`Alexa-Cookie: Cookies handled: ${JSON.stringify(proxyCookies)}`);
312
312
 
313
313
  if (
314
314
  (proxyRes.socket && proxyRes.socket._host === `www.${_options.baseAmazonPage}` && proxyRes.socket.parser.outgoing && proxyRes.socket.parser.outgoing.method === 'GET' && proxyRes.socket.parser.outgoing.path.startsWith('/ap/maplanding')) ||
@@ -324,8 +324,8 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
324
324
  proxyRes.headers.location = `http://${_options.proxyOwnIp}:${_options.proxyPort}/cookie-success`;
325
325
  delete proxyRes.headers.referer;
326
326
 
327
- _options.logger && _options.logger('Alexa-Cookie: Proxy catched cookie: ' + proxyCookies);
328
- _options.logger && _options.logger('Alexa-Cookie: Proxy catched parameters: ' + JSON.stringify(queryParams));
327
+ _options.logger && _options.logger(`Alexa-Cookie: Proxy catched cookie: ${proxyCookies}`);
328
+ _options.logger && _options.logger(`Alexa-Cookie: Proxy catched parameters: ${JSON.stringify(queryParams)}`);
329
329
 
330
330
  callbackCookie && callbackCookie(null, {
331
331
  'loginCookie': proxyCookies,
@@ -340,12 +340,12 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
340
340
 
341
341
  // If we detect a redirect, rewrite the location header
342
342
  if (proxyRes.headers.location) {
343
- _options.logger && _options.logger('Redirect: Original Location ----> ' + proxyRes.headers.location);
343
+ _options.logger && _options.logger(`Redirect: Original Location ----> ${proxyRes.headers.location}`);
344
344
  proxyRes.headers.location = replaceHosts(proxyRes.headers.location);
345
345
  if (reqestHost && proxyRes.headers.location.startsWith('/')) {
346
- proxyRes.headers.location = `http://${_options.proxyOwnIp}:${_options.proxyPort}/` + reqestHost + proxyRes.headers.location;
346
+ proxyRes.headers.location = `http://${_options.proxyOwnIp}:${_options.proxyPort}/${reqestHost}${proxyRes.headers.location}`;
347
347
  }
348
- _options.logger && _options.logger('Redirect: Final Location ----> ' + proxyRes.headers.location);
348
+ _options.logger && _options.logger(`Redirect: Final Location ----> ${proxyRes.headers.location}`);
349
349
  return;
350
350
  }
351
351
 
@@ -374,12 +374,16 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
374
374
  app.get('/cookie-success', function(req, res) {
375
375
  res.send(_options.proxyCloseWindowHTML);
376
376
  });
377
+ if (_options.proxyPort< 1 || _options.proxyPort > 65535) {
378
+ _options.logger && _options.logger(`Alexa-Cookie: Error: Port ${_options.proxyPort} invalid. Use random port.`);
379
+ _options.proxyPort = undefined;
380
+ }
377
381
  const server = app.listen(_options.proxyPort, _options.proxyListenBind, function() {
378
- _options.logger && _options.logger('Alexa-Cookie: Proxy-Server listening on port ' + server.address().port);
382
+ _options.logger && _options.logger(`Alexa-Cookie: Proxy-Server listening on port ${server.address().port}`);
379
383
  callbackListening && callbackListening(server);
380
384
  callbackListening = null;
381
385
  }).on('error', err => {
382
- _options.logger && _options.logger('Alexa-Cookie: Proxy-Server Error: ' + err);
386
+ _options.logger && _options.logger(`Alexa-Cookie: Proxy-Server Error: ${err}`);
383
387
  callbackListening && callbackListening(null);
384
388
  callbackListening = null;
385
389
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alexa-cookie2",
3
- "version": "4.2.0",
3
+ "version": "5.0.1",
4
4
  "description": "Generate Cookie and CSRF for Alexa Remote",
5
5
  "author": {
6
6
  "name": "Apollon77",
@@ -21,7 +21,7 @@
21
21
  "layla.amazon.de"
22
22
  ],
23
23
  "dependencies": {
24
- "cookie": "^0.5.0",
24
+ "cookie": "^0.6.0",
25
25
  "express": "^4.18.2",
26
26
  "http-proxy-middleware": "^2.0.6",
27
27
  "http-proxy-response-rewrite": "^0.0.1",
@@ -31,7 +31,7 @@
31
31
  "devDependencies": {
32
32
  "@alcalzone/release-script": "^3.6.0",
33
33
  "@alcalzone/release-script-plugin-license": "^3.5.9",
34
- "eslint": "^8.46.0"
34
+ "eslint": "^8.54.0"
35
35
  },
36
36
  "repository": {
37
37
  "type": "git",
@@ -44,7 +44,7 @@
44
44
  "release": "release-script"
45
45
  },
46
46
  "engines": {
47
- "node": ">=12.0.0"
47
+ "node": ">=16.0.0"
48
48
  },
49
49
  "main": "alexa-cookie.js",
50
50
  "readmeFilename": "readme.md"