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 +15 -1
- package/alexa-cookie.js +105 -76
- package/example/example.js +8 -3
- package/lib/proxy.js +29 -25
- package/package.json +4 -4
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.
|
|
23
|
-
const apiCallUserAgent = 'AmazonWebView/Amazon Alexa/2.2.
|
|
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 = `
|
|
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%
|
|
451
|
-
'os_version': '
|
|
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
|
-
|
|
540
|
-
|
|
534
|
+
registerTokenCapabilities(accessToken, () => {
|
|
535
|
+
/*
|
|
536
|
+
Get Amazon Marketplace Country
|
|
537
|
+
*/
|
|
541
538
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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
|
-
|
|
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
|
-
|
|
565
|
+
Cookie = addCookies(Cookie, response.headers);
|
|
569
566
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
587
|
-
if (err) {
|
|
588
|
-
callback && callback(err, null);
|
|
589
|
-
}
|
|
581
|
+
loginData.loginCookie = Cookie;
|
|
590
582
|
|
|
591
|
-
loginData.localCookie
|
|
592
|
-
getCSRFFromCookies(loginData.localCookie, _options, (err, resData) => {
|
|
583
|
+
getLocalCookies(loginData.amazonPage, loginData.refreshToken, (err, localCookie) => {
|
|
593
584
|
if (err) {
|
|
594
|
-
callback && callback(
|
|
595
|
-
return;
|
|
585
|
+
callback && callback(err, null);
|
|
596
586
|
}
|
|
597
|
-
|
|
598
|
-
loginData.
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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.
|
|
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': '
|
|
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.
|
|
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': '
|
|
721
|
-
'current_version': '6.12.
|
|
749
|
+
'di.os.version': '16.6',
|
|
750
|
+
'current_version': '6.12.4'
|
|
722
751
|
};
|
|
723
752
|
|
|
724
753
|
const options = {
|
package/example/example.js
CHANGED
|
@@ -8,12 +8,17 @@ const alexaCookie = require('../alexa-cookie');
|
|
|
8
8
|
|
|
9
9
|
const config = {
|
|
10
10
|
logger: console.log,
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 +=
|
|
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 +=
|
|
263
|
+
proxyCookies += `; ${reqCookie}`;
|
|
264
264
|
}
|
|
265
|
-
_options.logger && _options.logger(
|
|
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(
|
|
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') !==
|
|
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(
|
|
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(
|
|
300
|
-
_options.proxyLogLevel === 'debug' && _options.logger && _options.logger(
|
|
301
|
-
_options.proxyLogLevel === 'debug' && _options.logger && _options.logger(
|
|
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(
|
|
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(
|
|
328
|
-
_options.logger && _options.logger(
|
|
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(
|
|
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}
|
|
346
|
+
proxyRes.headers.location = `http://${_options.proxyOwnIp}:${_options.proxyPort}/${reqestHost}${proxyRes.headers.location}`;
|
|
347
347
|
}
|
|
348
|
-
_options.logger && _options.logger(
|
|
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(
|
|
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(
|
|
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": "
|
|
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.
|
|
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.
|
|
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": ">=
|
|
47
|
+
"node": ">=16.0.0"
|
|
48
48
|
},
|
|
49
49
|
"main": "alexa-cookie.js",
|
|
50
50
|
"readmeFilename": "readme.md"
|