alexa-cookie2 4.1.3 → 5.0.0

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018-2022 Apollon77 <ingo@fischer-ka.de>
3
+ Copyright (c) 2018-2023 Apollon77 <ingo@fischer-ka.de>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -42,6 +42,9 @@ Please use the new method "refreshAlexaCookie" to refresh the cookie data. It ta
42
42
 
43
43
  Since 4.0.0 of this library a new key called "macDms" is also returned when cookies are generated or refreshed. This is (right now Oct 2021) needed to use the Push Connection (alexa-remote library). Better strt also persisting this field, might be needed more later on.
44
44
 
45
+ ## Troubleshooting
46
+ If you still use the SMS based 2FA flow then this might now work. Please update the 2fa method in the amazon settings to the current process.
47
+
45
48
  ## Thanks:
46
49
  A big thanks go to soef for the initial version of this library and to many other community users to support in finding out what Amazon changes here and there.
47
50
 
@@ -49,6 +52,13 @@ Partly based on [Amazon Alexa Remote Control](http://blog.loetzimmer.de/2017/10/
49
52
  Thank you for that work.
50
53
 
51
54
  ## Changelog:
55
+ ### 5.0.0 (2023-09-08)
56
+ * IMPORTANT: Node.js 16 is now required minimum Node.js version!
57
+ * (Apollon77) Enhance registration process by also registering the app capabilities to allow usage of new HTTP/2 push connection
58
+
59
+ ### 4.2.0 (2023-08-08)
60
+ * (Hive) Adds the ability to alter the close proxy message
61
+
52
62
  ### 4.1.3 (2022-08-03)
53
63
  * (Apollon77) Fix device registration and token exchange in other regions
54
64
  * (Apollon77) Use the chosen App name also for refreshing of tokens
package/alexa-cookie.js CHANGED
@@ -16,10 +16,11 @@ const defaultAmazonPage = 'amazon.de';
16
16
  const defaultUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36';
17
17
  const defaultUserAgentLinux = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36';
18
18
  //const defaultUserAgentMacOs = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36';
19
+ const defaultProxyCloseWindowHTML = '<b>Amazon Alexa Cookie successfully retrieved. You can close the browser.</b>';
19
20
  const defaultAcceptLanguage = 'de-DE';
20
21
 
21
- const apiCallVersion = '2.2.485407.0';
22
- 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';
23
24
  const defaultAppName = 'ioBroker Alexa2';
24
25
 
25
26
  const csrfOptions = [
@@ -177,8 +178,11 @@ function AlexaCookie() {
177
178
  _options.logger && _options.logger(`Alexa-Cookie: Use as User-Agent: ${_options.userAgent}`);
178
179
 
179
180
  _options.acceptLanguage = _options.acceptLanguage || defaultAcceptLanguage;
181
+
180
182
  _options.logger && _options.logger(`Alexa-Cookie: Use as Accept-Language: ${_options.acceptLanguage}`);
181
183
 
184
+ _options.proxyCloseWindowHTML = _options.proxyCloseWindowHTML || defaultProxyCloseWindowHTML;
185
+
182
186
  if (_options.setupProxy && !_options.proxyOwnIp) {
183
187
  _options.logger && _options.logger('Alexa-Cookie: Own-IP Setting missing for Proxy. Disabling!');
184
188
  _options.setupProxy = false;
@@ -431,12 +435,7 @@ function AlexaCookie() {
431
435
  'customer_info'
432
436
  ],
433
437
  'cookies': {
434
- 'website_cookies': [
435
- /*{
436
- "Value": cookies["session-id-time"],
437
- "Name": "session-id-time"
438
- }*/
439
- ],
438
+ 'website_cookies': [],
440
439
  'domain': `.${_options.baseAmazonPage}`
441
440
  },
442
441
  'registration_data': {
@@ -444,7 +443,7 @@ function AlexaCookie() {
444
443
  'app_version': apiCallVersion,
445
444
  'device_type': 'A2IVLV5VM2W81',
446
445
  'device_name': '%FIRST_NAME%\u0027s%DUPE_STRATEGY_1ST%ioBroker Alexa2',
447
- 'os_version': '15.5',
446
+ 'os_version': '16.6',
448
447
  'device_serial': deviceSerial,
449
448
  'device_model': 'iPhone',
450
449
  'app_name': _options.deviceAppName,
@@ -520,6 +519,7 @@ function AlexaCookie() {
520
519
  }
521
520
  Cookie = addCookies(Cookie, response.headers);
522
521
  loginData.refreshToken = body.response.success.tokens.bearer.refresh_token;
522
+ const accessToken = body.response.success.tokens.bearer.access_token;
523
523
  loginData.tokenDate = Date.now();
524
524
  loginData.macDms = body.response.success.tokens.mac_dms;
525
525
 
@@ -531,78 +531,110 @@ function AlexaCookie() {
531
531
  Cookie = addCookies(Cookie, {'set-cookie': newCookies});
532
532
  }
533
533
 
534
- /*
535
- Get Amazon Marketplace Country
536
- */
534
+ registerTokenCapabilities(accessToken, () => {
535
+ /*
536
+ Get Amazon Marketplace Country
537
+ */
537
538
 
538
- const options = {
539
- host: `alexa.${_options.baseAmazonPage}`,
540
- path: `/api/users/me?platform=ios&version=${apiCallVersion}`,
541
- method: 'GET',
542
- headers: {
543
- 'User-Agent': apiCallUserAgent,
544
- 'Accept-Language': _options.acceptLanguage,
545
- 'Accept-Charset': 'utf-8',
546
- 'Connection': 'keep-alive',
547
- 'Accept': 'application/json',
548
- 'Cookie': Cookie
549
- }
550
- };
551
- _options.logger && _options.logger('Alexa-Cookie: Get User data');
552
- _options.logger && _options.logger(JSON.stringify(options));
553
- request(options, (error, response, body) => {
554
- if (!error) {
555
- try {
556
- if (typeof body !== 'object') body = JSON.parse(body);
557
- } catch (err) {
558
- _options.logger && _options.logger(`Get User data Response: ${JSON.stringify(body)}`);
559
- callback && callback(err, null);
560
- 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
561
550
  }
562
- _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)}`);
563
564
 
564
- Cookie = addCookies(Cookie, response.headers);
565
+ Cookie = addCookies(Cookie, response.headers);
565
566
 
566
- if (body.marketPlaceDomainName) {
567
- const pos = body.marketPlaceDomainName.indexOf('.');
568
- 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');
569
579
  }
570
- loginData.amazonPage = _options.amazonPage;
571
- } else if (error && (!_options || !_options.amazonPage)) {
572
- callback && callback(error, null);
573
- return;
574
- } else if (error && (!_options.formerRegistrationData || !_options.formerRegistrationData.amazonPage) && _options.amazonPage) {
575
- _options.logger && _options.logger(`Continue with externally set amazonPage: ${_options.amazonPage}`);
576
- } else if (error) {
577
- _options.logger && _options.logger('Ignore error while getting user data and amazonPage because previously set amazonPage is available');
578
- }
579
-
580
- loginData.loginCookie = Cookie;
581
580
 
582
- getLocalCookies(loginData.amazonPage, loginData.refreshToken, (err, localCookie) => {
583
- if (err) {
584
- callback && callback(err, null);
585
- }
581
+ loginData.loginCookie = Cookie;
586
582
 
587
- loginData.localCookie = localCookie;
588
- getCSRFFromCookies(loginData.localCookie, _options, (err, resData) => {
583
+ getLocalCookies(loginData.amazonPage, loginData.refreshToken, (err, localCookie) => {
589
584
  if (err) {
590
- callback && callback(new Error(`Error getting csrf for ${loginData.amazonPage}`), null);
591
- return;
585
+ callback && callback(err, null);
592
586
  }
593
- loginData.localCookie = resData.cookie;
594
- loginData.csrf = resData.csrf;
595
- delete loginData.accessToken;
596
- delete loginData.authorization_code;
597
- delete loginData.verifier;
598
- _options.logger && _options.logger(`Final Registration Result: ${JSON.stringify(loginData)}`);
599
- 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
+ });
600
603
  });
601
604
  });
602
605
  });
603
606
  });
604
607
  };
605
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
+
606
638
  const getLocalCookies = (amazonPage, refreshToken, callback) => {
607
639
  Cookie = ''; // Reset because we are switching domains
608
640
  /*
@@ -617,10 +649,9 @@ function AlexaCookie() {
617
649
  'requested_token_type': 'auth_cookies',
618
650
  'source_token_type': 'refresh_token',
619
651
  'di.hw.version': 'iPhone',
620
- 'di.sdk.version': '6.12.3',
621
- 'cookies': Buffer.from(`{„cookies“:{".${amazonPage}":[]}}`).toString('base64'),
652
+ 'di.sdk.version': '6.12.4',
622
653
  'app_name': _options.deviceAppName || defaultAppName,
623
- 'di.os.version': '15.5'
654
+ 'di.os.version': '16.6'
624
655
  };
625
656
  const options = {
626
657
  host: `www.${amazonPage}`,
@@ -632,7 +663,9 @@ function AlexaCookie() {
632
663
  'Accept-Charset': 'utf-8',
633
664
  'Connection': 'keep-alive',
634
665
  'Content-Type': 'application/x-www-form-urlencoded',
635
- 'Accept': '*/*'
666
+ 'Accept': '*/*',
667
+ 'Cookie': Cookie,
668
+ 'x-amzn-identity-auth-domain': `api.${amazonPage}`
636
669
  },
637
670
  body: querystring.stringify(exchangeParams, null, null, {
638
671
  encodeURIComponent: encodeURIComponent
@@ -705,7 +738,7 @@ function AlexaCookie() {
705
738
  const refreshData = {
706
739
  'app_name': _options.deviceAppName || defaultAppName,
707
740
  'app_version': apiCallVersion,
708
- 'di.sdk.version': '6.12.3',
741
+ 'di.sdk.version': '6.12.4',
709
742
  'source_token': _options.formerRegistrationData.refreshToken,
710
743
  'package_name': 'com.amazon.echo',
711
744
  'di.hw.version': 'iPhone',
@@ -713,8 +746,8 @@ function AlexaCookie() {
713
746
  'requested_token_type': 'access_token',
714
747
  'source_token_type': 'refresh_token',
715
748
  'di.os.name': 'iOS',
716
- 'di.os.version': '15.5',
717
- 'current_version': '6.12.3'
749
+ 'di.os.version': '16.6',
750
+ 'current_version': '6.12.4'
718
751
  };
719
752
 
720
753
  const options = {
@@ -21,7 +21,8 @@ const config = {
21
21
  amazonPageProxyLanguage: 'de_DE', // optional: language to be used for the Amazon Sign-in page the proxy calls. default is "de_DE")
22
22
  deviceAppName: '...', // optional: name of the device app name which will be registered with Amazon, leave empty to use a default one
23
23
  formerDataStorePath: '...', // optional: overwrite path where some of the formerRegistrationData are persisted to optimize against Amazon security measures
24
- formerRegistrationData: { ... } // optional/preferred: provide the result object from subsequent proxy usages here and some generated data will be reused for next proxy call too
24
+ formerRegistrationData: { ... }, // optional/preferred: provide the result object from subsequent proxy usages here and some generated data will be reused for next proxy call too
25
+ proxyCloseWindowHTML: '...' // optional: use in order to override the default html displayed when the proxy window can be closed, default is '<b>Amazon Alexa Cookie successfully retrieved. You can close the browser.</b>'
25
26
  };
26
27
 
27
28
 
package/lib/proxy.js CHANGED
@@ -372,7 +372,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
372
372
 
373
373
  app.use(myProxy);
374
374
  app.get('/cookie-success', function(req, res) {
375
- res.send('<b>Amazon Alexa Cookie successfully retrieved. You can close the browser.</b>');
375
+ res.send(_options.proxyCloseWindowHTML);
376
376
  });
377
377
  const server = app.listen(_options.proxyPort, _options.proxyListenBind, function() {
378
378
  _options.logger && _options.logger('Alexa-Cookie: Proxy-Server listening on port ' + server.address().port);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alexa-cookie2",
3
- "version": "4.1.3",
3
+ "version": "5.0.0",
4
4
  "description": "Generate Cookie and CSRF for Alexa Remote",
5
5
  "author": {
6
6
  "name": "Apollon77",
@@ -22,16 +22,16 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "cookie": "^0.5.0",
25
- "express": "^4.18.1",
25
+ "express": "^4.18.2",
26
26
  "http-proxy-middleware": "^2.0.6",
27
27
  "http-proxy-response-rewrite": "^0.0.1",
28
28
  "https": "^1.0.0",
29
29
  "querystring": "^0.2.1"
30
30
  },
31
31
  "devDependencies": {
32
- "@alcalzone/release-script": "^3.5.9",
32
+ "@alcalzone/release-script": "^3.6.0",
33
33
  "@alcalzone/release-script-plugin-license": "^3.5.9",
34
- "eslint": "^8.20.0"
34
+ "eslint": "^8.48.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"