alexa-cookie2 3.4.3 → 4.0.2
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/.eslintrc.json +40 -0
- package/.releaseconfig.json +3 -0
- package/LICENSE +1 -1
- package/README.md +22 -7
- package/alexa-cookie.js +85 -77
- package/lib/proxy.js +33 -20
- package/package.json +10 -5
- package/.commitmessage +0 -3
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"env": {
|
|
4
|
+
"es6": true,
|
|
5
|
+
"node": true,
|
|
6
|
+
"mocha": true
|
|
7
|
+
},
|
|
8
|
+
"extends": [
|
|
9
|
+
"eslint:recommended"
|
|
10
|
+
],
|
|
11
|
+
"plugins": [],
|
|
12
|
+
"rules": {
|
|
13
|
+
"indent": [
|
|
14
|
+
"error",
|
|
15
|
+
4,
|
|
16
|
+
{
|
|
17
|
+
"SwitchCase": 1
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"no-console": "off",
|
|
21
|
+
"no-var": "error",
|
|
22
|
+
"no-trailing-spaces": "error",
|
|
23
|
+
"prefer-const": "error",
|
|
24
|
+
"quotes": [
|
|
25
|
+
"error",
|
|
26
|
+
"single",
|
|
27
|
+
{
|
|
28
|
+
"avoidEscape": true,
|
|
29
|
+
"allowTemplateLiterals": true
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"semi": [
|
|
33
|
+
"error",
|
|
34
|
+
"always"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"parserOptions": {
|
|
38
|
+
"ecmaVersion": 2018
|
|
39
|
+
}
|
|
40
|
+
}
|
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2018-
|
|
3
|
+
Copyright (c) 2018-2022 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
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# alexa-cookie
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/alexa-cookie2)
|
|
4
|
+
[](https://www.npmjs.com/package/alexa-cookie2)
|
|
5
|
+

|
|
6
|
+
|
|
3
7
|
Library to generate/retrieve a cookie including a csrf for alexa remote
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-->
|
|
9
|
-
[](https://github.com/soef/alexa-remote/blob/master/LICENSE)
|
|
9
|
+
## Disclaimer
|
|
10
|
+
**All product and company names or logos are trademarks™ or registered® trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them or any associated subsidiaries! This personal project is maintained in spare time and has no business goal.**
|
|
11
|
+
**ALEXA is a trademark of AMAZON TECHNOLOGIES, INC.**
|
|
10
12
|
|
|
11
13
|
## Description
|
|
12
14
|
This library can be used to get the cookies needed to access Amazon Alexa services from outside. It authenticates with Amazon and gathers all needed details. These details are returned in the callback.
|
|
@@ -36,15 +38,28 @@ Important for the further interaction with alexa are the keys "localCookie" (sam
|
|
|
36
38
|
|
|
37
39
|
If you not do this a new device is created each time the proxy is used which can end up in having many unused devices (such a device is like a mobile phone where you use the Alexa App with).
|
|
38
40
|
|
|
39
|
-
Please use the new method "refreshAlexaCookie" to refresh the cookie data. It takes the same options object as the other method and requires the key "formerRegistrationData". It returns an updated object will all data as above. Please also store this and provide for
|
|
41
|
+
Please use the new method "refreshAlexaCookie" to refresh the cookie data. It takes the same options object as the other method and requires the key "formerRegistrationData". It returns an updated object will all data as above. Please also store this and provide for subsequent calls!
|
|
42
|
+
|
|
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.
|
|
40
44
|
|
|
41
45
|
## Thanks:
|
|
42
|
-
A big thanks go to soef for the initial version of this library.
|
|
46
|
+
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.
|
|
43
47
|
|
|
44
48
|
Partly based on [Amazon Alexa Remote Control](http://blog.loetzimmer.de/2017/10/amazon-alexa-hort-auf-die-shell-echo.html) (PLAIN shell) and [alexa-remote-control](https://github.com/thorsten-gehrig/alexa-remote-control) and the the Proxy idea from [OpenHab-Addon](https://github.com/openhab/openhab2-addons/blob/f54c9b85016758ff6d271b62d255bbe41a027928/addons/binding/org.openhab.binding.amazonechocontrol). Also the new way to refresh cookie and all needed changes were developed in close cooperation with @mgeramb
|
|
45
49
|
Thank you for that work.
|
|
46
50
|
|
|
47
51
|
## Changelog:
|
|
52
|
+
### 4.0.2 (2022-06-30)
|
|
53
|
+
* (Apollon77) Prevent potential crash cases
|
|
54
|
+
|
|
55
|
+
### 4.0.1 (2021-10-11)
|
|
56
|
+
* (Apollon77) Adjust call headers
|
|
57
|
+
|
|
58
|
+
### 4.0.0 (2021-10-11)
|
|
59
|
+
* IMPORTANT: Node.js 10 support is dropped, supports LTS versions of Node.js starting with 12.x
|
|
60
|
+
* (Apollon77) Add support to get macDms with relevant data from the device registration process to use in push connection.
|
|
61
|
+
* (adn77) Update Login Flow to use match to most current Alexa App flow using code auth
|
|
62
|
+
* (Apollon77) Update deps, drop Node.js 10 support
|
|
48
63
|
|
|
49
64
|
### 3.4.3 (2021-04-18)
|
|
50
65
|
* (Apollon77) handle potential crash case (Sentry IOBROKER-ALEXA2-86)
|
package/alexa-cookie.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/* jshint -W097 */
|
|
2
|
-
/* jshint -W030 */
|
|
3
|
-
/* jshint strict: false */
|
|
4
|
-
/* jslint node: true */
|
|
5
|
-
/* jslint esversion: 6 */
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
1
|
/**
|
|
9
2
|
* partly based on Amazon Alexa Remote Control (PLAIN shell)
|
|
10
3
|
* http://blog.loetzimmer.de/2017/10/amazon-alexa-hort-auf-die-shell-echo.html AND on
|
|
@@ -57,7 +50,7 @@ function AlexaCookie() {
|
|
|
57
50
|
}
|
|
58
51
|
}
|
|
59
52
|
Cookie = '';
|
|
60
|
-
for (
|
|
53
|
+
for (const name in cookies) {
|
|
61
54
|
if (!cookies.hasOwnProperty(name)) continue;
|
|
62
55
|
Cookie += name + '=' + cookies[name] + '; ';
|
|
63
56
|
}
|
|
@@ -83,14 +76,14 @@ function AlexaCookie() {
|
|
|
83
76
|
removeContentLength = true;
|
|
84
77
|
}
|
|
85
78
|
|
|
86
|
-
|
|
87
|
-
let body =
|
|
79
|
+
const req = https.request(options, (res) => {
|
|
80
|
+
let body = '';
|
|
88
81
|
info.requests.push({options: options, response: res});
|
|
89
82
|
|
|
90
83
|
if (options.followRedirects !== false && res.statusCode >= 300 && res.statusCode < 400) {
|
|
91
84
|
_options.logger && _options.logger('Alexa-Cookie: Response (' + res.statusCode + ')' + (res.headers.location ? ' - Redirect to ' + res.headers.location : ''));
|
|
92
85
|
//options.url = res.headers.location;
|
|
93
|
-
|
|
86
|
+
const u = url.parse(res.headers.location);
|
|
94
87
|
if (u.host) options.host = u.host;
|
|
95
88
|
options.path = u.path;
|
|
96
89
|
options.method = 'GET';
|
|
@@ -127,11 +120,11 @@ function AlexaCookie() {
|
|
|
127
120
|
const getFields = body => {
|
|
128
121
|
body = body.replace(/[\n\r]/g, ' ');
|
|
129
122
|
let re = /^.*?("hidden"\s*name=".*$)/;
|
|
130
|
-
|
|
123
|
+
const ar = re.exec(body);
|
|
131
124
|
if (!ar || ar.length < 2) return {};
|
|
132
125
|
let h;
|
|
133
126
|
re = /.*?name="([^"]+)"[\s^\s]*value="([^"]+).*?"/g;
|
|
134
|
-
|
|
127
|
+
const data = {};
|
|
135
128
|
while ((h = re.exec(ar[1])) !== null) {
|
|
136
129
|
if (h[1] !== 'rememberMe') {
|
|
137
130
|
data[h[1]] = h[2];
|
|
@@ -164,7 +157,7 @@ function AlexaCookie() {
|
|
|
164
157
|
}
|
|
165
158
|
|
|
166
159
|
if (!_options.userAgent) {
|
|
167
|
-
|
|
160
|
+
const platform = os.platform();
|
|
168
161
|
if (platform === 'win32') {
|
|
169
162
|
_options.userAgent = defaultUserAgent;
|
|
170
163
|
}
|
|
@@ -205,7 +198,7 @@ function AlexaCookie() {
|
|
|
205
198
|
|
|
206
199
|
function csrfTry() {
|
|
207
200
|
const path = csrfUrls.shift();
|
|
208
|
-
|
|
201
|
+
const options = {
|
|
209
202
|
'host': 'alexa.' + _options.amazonPage,
|
|
210
203
|
'path': path,
|
|
211
204
|
'method': 'GET',
|
|
@@ -223,8 +216,8 @@ function AlexaCookie() {
|
|
|
223
216
|
_options.logger && _options.logger('Alexa-Cookie: Step 4: get CSRF via ' + path);
|
|
224
217
|
request(options, (error, response) => {
|
|
225
218
|
cookie = addCookies(cookie, response ? response.headers : null);
|
|
226
|
-
|
|
227
|
-
|
|
219
|
+
const ar = /csrf=([^;]+)/.exec(cookie);
|
|
220
|
+
const csrf = ar ? ar[1] : undefined;
|
|
228
221
|
_options.logger && _options.logger('Alexa-Cookie: Result: csrf=' + csrf + ', Cookie=' + cookie);
|
|
229
222
|
if (!csrf && csrfUrls.length) {
|
|
230
223
|
csrfTry();
|
|
@@ -268,7 +261,7 @@ function AlexaCookie() {
|
|
|
268
261
|
|
|
269
262
|
if (!_options.proxyOnly) {
|
|
270
263
|
// get first cookie and write redirection target into referer
|
|
271
|
-
|
|
264
|
+
const options = {
|
|
272
265
|
host: 'alexa.' + _options.amazonPage,
|
|
273
266
|
path: '',
|
|
274
267
|
method: 'GET',
|
|
@@ -288,10 +281,10 @@ function AlexaCookie() {
|
|
|
288
281
|
return;
|
|
289
282
|
}
|
|
290
283
|
|
|
291
|
-
|
|
284
|
+
const lastRequestOptions = info.requests[info.requests.length - 1].options;
|
|
292
285
|
// login empty to generate session
|
|
293
286
|
Cookie = addCookies(Cookie, response.headers);
|
|
294
|
-
|
|
287
|
+
const options = {
|
|
295
288
|
host: 'www.' + _options.amazonPage,
|
|
296
289
|
path: '/ap/signin',
|
|
297
290
|
method: 'POST',
|
|
@@ -322,7 +315,7 @@ function AlexaCookie() {
|
|
|
322
315
|
options.path = '/ap/signin';
|
|
323
316
|
options.method = 'POST';
|
|
324
317
|
options.headers.Cookie = Cookie = addCookies(Cookie, response.headers);
|
|
325
|
-
|
|
318
|
+
const ar = options.headers.Cookie.match(/session-id=([^;]+)/);
|
|
326
319
|
options.headers.Referer = `https://www.${_options.amazonPage}/ap/signin/${ar[1]}`;
|
|
327
320
|
options.body = getFields(body);
|
|
328
321
|
options.body.email = email || '';
|
|
@@ -336,7 +329,7 @@ function AlexaCookie() {
|
|
|
336
329
|
return;
|
|
337
330
|
}
|
|
338
331
|
|
|
339
|
-
|
|
332
|
+
const lastRequestOptions = info.requests[info.requests.length - 1].options;
|
|
340
333
|
|
|
341
334
|
// check whether the login has been successful or exit otherwise
|
|
342
335
|
if (!lastRequestOptions.host.startsWith('alexa') || !lastRequestOptions.path.endsWith('.html')) {
|
|
@@ -389,7 +382,7 @@ function AlexaCookie() {
|
|
|
389
382
|
}
|
|
390
383
|
|
|
391
384
|
function prepareResult(err, data) {
|
|
392
|
-
if (err || !data.
|
|
385
|
+
if (err || !data.authorization_code) {
|
|
393
386
|
callback && callback(err, data.loginCookie);
|
|
394
387
|
return;
|
|
395
388
|
}
|
|
@@ -422,62 +415,75 @@ function AlexaCookie() {
|
|
|
422
415
|
*/
|
|
423
416
|
|
|
424
417
|
const registerData = {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
418
|
+
'requested_extensions': [
|
|
419
|
+
'device_info',
|
|
420
|
+
'customer_info'
|
|
428
421
|
],
|
|
429
|
-
|
|
430
|
-
|
|
422
|
+
'cookies': {
|
|
423
|
+
'website_cookies': [
|
|
431
424
|
/*{
|
|
432
425
|
"Value": cookies["session-id-time"],
|
|
433
426
|
"Name": "session-id-time"
|
|
434
427
|
}*/
|
|
435
428
|
],
|
|
436
|
-
|
|
429
|
+
'domain': '.' + _options.baseAmazonPage
|
|
437
430
|
},
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
431
|
+
'registration_data': {
|
|
432
|
+
'domain': 'Device',
|
|
433
|
+
'app_version': '2.2.443692.0',
|
|
434
|
+
'device_type': 'A2IVLV5VM2W81',
|
|
435
|
+
'device_name': '%FIRST_NAME%\u0027s%DUPE_STRATEGY_1ST%ioBroker Alexa2',
|
|
436
|
+
'os_version': '14.8',
|
|
437
|
+
'device_serial': deviceSerial,
|
|
438
|
+
'device_model': 'iPhone',
|
|
439
|
+
'app_name': 'ioBroker Alexa2',
|
|
440
|
+
'software_version': '1'
|
|
448
441
|
},
|
|
449
|
-
|
|
450
|
-
|
|
442
|
+
'auth_data': {
|
|
443
|
+
// Filled below
|
|
451
444
|
},
|
|
452
|
-
|
|
453
|
-
|
|
445
|
+
'user_context_map': {
|
|
446
|
+
'frc': cookies.frc
|
|
454
447
|
},
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
448
|
+
'requested_token_type': [
|
|
449
|
+
'bearer',
|
|
450
|
+
'mac_dms',
|
|
451
|
+
'website_cookies'
|
|
459
452
|
]
|
|
460
453
|
};
|
|
461
|
-
|
|
454
|
+
if (loginData.accessToken) {
|
|
455
|
+
registerData.auth_data = {
|
|
456
|
+
'access_token': loginData.accessToken
|
|
457
|
+
};
|
|
458
|
+
} else if (loginData.authorization_code && loginData.verifier) {
|
|
459
|
+
registerData.auth_data = {
|
|
460
|
+
'client_id' : loginData.deviceId,
|
|
461
|
+
'authorization_code' : loginData.authorization_code,
|
|
462
|
+
'code_verifier' : loginData.verifier,
|
|
463
|
+
'code_algorithm' : 'SHA-256',
|
|
464
|
+
'client_domain' : 'DeviceLegacy'
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
for (const key in cookies) {
|
|
462
468
|
if (!cookies.hasOwnProperty(key)) continue;
|
|
463
469
|
registerData.cookies.website_cookies.push({
|
|
464
|
-
|
|
465
|
-
|
|
470
|
+
'Value': cookies[key],
|
|
471
|
+
'Name': key
|
|
466
472
|
});
|
|
467
473
|
}
|
|
468
474
|
|
|
469
|
-
|
|
475
|
+
const options = {
|
|
470
476
|
host: 'api.' + _options.baseAmazonPage,
|
|
471
477
|
path: '/auth/register',
|
|
472
478
|
method: 'POST',
|
|
473
479
|
headers: {
|
|
474
|
-
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.
|
|
480
|
+
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
|
|
475
481
|
'Accept-Language': _options.acceptLanguage,
|
|
476
482
|
'Accept-Charset': 'utf-8',
|
|
477
483
|
'Connection': 'keep-alive',
|
|
478
484
|
'Content-Type': 'application/json',
|
|
479
485
|
'Cookie': loginData.loginCookie,
|
|
480
|
-
'Accept': '
|
|
486
|
+
'Accept': 'application/json',
|
|
481
487
|
'x-amzn-identity-auth-domain': 'api.' + _options.baseAmazonPage
|
|
482
488
|
},
|
|
483
489
|
body: JSON.stringify(registerData)
|
|
@@ -505,18 +511,18 @@ function AlexaCookie() {
|
|
|
505
511
|
Cookie = addCookies(Cookie, response.headers);
|
|
506
512
|
loginData.refreshToken = body.response.success.tokens.bearer.refresh_token;
|
|
507
513
|
loginData.tokenDate = Date.now();
|
|
508
|
-
|
|
514
|
+
loginData.macDms = body.response.success.tokens.mac_dms;
|
|
509
515
|
|
|
510
516
|
/*
|
|
511
517
|
Get Amazon Marketplace Country
|
|
512
518
|
*/
|
|
513
519
|
|
|
514
|
-
|
|
520
|
+
const options = {
|
|
515
521
|
host: 'alexa.' + _options.baseAmazonPage,
|
|
516
|
-
path: '/api/users/me?platform=ios&version=2.2.
|
|
522
|
+
path: '/api/users/me?platform=ios&version=2.2.443692.0',
|
|
517
523
|
method: 'GET',
|
|
518
524
|
headers: {
|
|
519
|
-
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.
|
|
525
|
+
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
|
|
520
526
|
'Accept-Language': _options.acceptLanguage,
|
|
521
527
|
'Accept-Charset': 'utf-8',
|
|
522
528
|
'Connection': 'keep-alive',
|
|
@@ -547,7 +553,7 @@ function AlexaCookie() {
|
|
|
547
553
|
} else if (error && (!_options || !_options.amazonPage)) {
|
|
548
554
|
callback && callback(error, null);
|
|
549
555
|
return;
|
|
550
|
-
} else if (error && !_options.formerRegistrationData.amazonPage && _options.amazonPage) {
|
|
556
|
+
} else if (error && (!_options.formerRegistrationData || !_options.formerRegistrationData.amazonPage) && _options.amazonPage) {
|
|
551
557
|
_options.logger && _options.logger('Continue with externally set amazonPage: ' + _options.amazonPage);
|
|
552
558
|
} else if (error) {
|
|
553
559
|
_options.logger && _options.logger('Ignore error while getting user data and amazonPage because previously set amazonPage is available');
|
|
@@ -569,7 +575,9 @@ function AlexaCookie() {
|
|
|
569
575
|
loginData.localCookie = resData.cookie;
|
|
570
576
|
loginData.csrf = resData.csrf;
|
|
571
577
|
delete loginData.accessToken;
|
|
572
|
-
|
|
578
|
+
delete loginData.authorization_code;
|
|
579
|
+
delete loginData.verifier;
|
|
580
|
+
_options.logger && _options.logger('Final Registration Result: ' + JSON.stringify(loginData));
|
|
573
581
|
callback && callback(null, loginData);
|
|
574
582
|
});
|
|
575
583
|
});
|
|
@@ -585,7 +593,7 @@ function AlexaCookie() {
|
|
|
585
593
|
|
|
586
594
|
const exchangeParams = {
|
|
587
595
|
'di.os.name': 'iOS',
|
|
588
|
-
'app_version': '2.2.
|
|
596
|
+
'app_version': '2.2.443692.0',
|
|
589
597
|
'domain': '.' + amazonPage,
|
|
590
598
|
'source_token': refreshToken,
|
|
591
599
|
'requested_token_type': 'auth_cookies',
|
|
@@ -596,12 +604,12 @@ function AlexaCookie() {
|
|
|
596
604
|
'app_name': 'Amazon Alexa',
|
|
597
605
|
'di.os.version': '11.4.1'
|
|
598
606
|
};
|
|
599
|
-
|
|
607
|
+
const options = {
|
|
600
608
|
host: 'www.' + amazonPage,
|
|
601
609
|
path: '/ap/exchangetoken',
|
|
602
610
|
method: 'POST',
|
|
603
611
|
headers: {
|
|
604
|
-
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.
|
|
612
|
+
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
|
|
605
613
|
'Accept-Language': _options.acceptLanguage,
|
|
606
614
|
'Accept-Charset': 'utf-8',
|
|
607
615
|
'Connection': 'keep-alive',
|
|
@@ -650,7 +658,7 @@ function AlexaCookie() {
|
|
|
650
658
|
|
|
651
659
|
});
|
|
652
660
|
let localCookie = '';
|
|
653
|
-
for (
|
|
661
|
+
for (const name in cookies) {
|
|
654
662
|
if (!cookies.hasOwnProperty(name)) continue;
|
|
655
663
|
localCookie += name + '=' + cookies[name] + '; ';
|
|
656
664
|
}
|
|
@@ -678,26 +686,26 @@ function AlexaCookie() {
|
|
|
678
686
|
initConfig();
|
|
679
687
|
|
|
680
688
|
const refreshData = {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
689
|
+
'app_name': 'ioBroker Alexa2',
|
|
690
|
+
'app_version': '2.2.443692.0',
|
|
691
|
+
'di.sdk.version': '6.10.0',
|
|
692
|
+
'source_token': _options.formerRegistrationData.refreshToken,
|
|
693
|
+
'package_name': 'com.amazon.echo',
|
|
694
|
+
'di.hw.version': 'iPhone',
|
|
695
|
+
'platform': 'iOS',
|
|
696
|
+
'requested_token_type': 'access_token',
|
|
697
|
+
'source_token_type': 'refresh_token',
|
|
698
|
+
'di.os.name': 'iOS',
|
|
699
|
+
'di.os.version': '14.8',
|
|
700
|
+
'current_version': '6.10.0'
|
|
693
701
|
};
|
|
694
702
|
|
|
695
|
-
|
|
703
|
+
const options = {
|
|
696
704
|
host: 'api.' + _options.baseAmazonPage,
|
|
697
705
|
path: '/auth/token',
|
|
698
706
|
method: 'POST',
|
|
699
707
|
headers: {
|
|
700
|
-
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.
|
|
708
|
+
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
|
|
701
709
|
'Accept-Language': _options.acceptLanguage,
|
|
702
710
|
'Accept-Charset': 'utf-8',
|
|
703
711
|
'Connection': 'keep-alive',
|
package/lib/proxy.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/* jshint strict: false */
|
|
4
4
|
/* jslint node: true */
|
|
5
5
|
/* jslint esversion: 6 */
|
|
6
|
-
|
|
6
|
+
'use strict';
|
|
7
7
|
|
|
8
8
|
const modifyResponse = require('http-proxy-response-rewrite');
|
|
9
9
|
const express = require('express');
|
|
@@ -27,7 +27,7 @@ function addCookies(Cookie, headers) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
Cookie = '';
|
|
30
|
-
for (
|
|
30
|
+
for (const name in cookies) {
|
|
31
31
|
if (!cookies.hasOwnProperty(name)) continue;
|
|
32
32
|
Cookie += name + '=' + cookies[name] + '; ';
|
|
33
33
|
}
|
|
@@ -90,7 +90,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
90
90
|
initialCookies.frc = _options.formerRegistrationData.frc;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
if (!_options.formerRegistrationData || !_options.formerRegistrationData[
|
|
93
|
+
if (!_options.formerRegistrationData || !_options.formerRegistrationData['map-md']) {
|
|
94
94
|
initialCookies['map-md'] = Buffer.from('{"device_user_dictionary":[],"device_registration_data":{"software_version":"1"},"app_identifier":{"app_version":"2.2.223830","bundle_id":"com.amazon.echo"}}').toString('base64');
|
|
95
95
|
}
|
|
96
96
|
else {
|
|
@@ -123,7 +123,19 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
123
123
|
// ignore
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
function base64URLEncode(str) {
|
|
127
|
+
return str.toString('base64')
|
|
128
|
+
.replace(/\+/g, '-')
|
|
129
|
+
.replace(/\//g, '_')
|
|
130
|
+
.replace(/=/g, '');
|
|
131
|
+
}
|
|
132
|
+
function sha256(buffer) {
|
|
133
|
+
return crypto.createHash('sha256').update(buffer).digest();
|
|
134
|
+
}
|
|
135
|
+
const code_verifier = base64URLEncode(crypto.randomBytes(32));
|
|
136
|
+
const code_challenge = base64URLEncode(sha256(code_verifier));
|
|
137
|
+
|
|
138
|
+
let proxyCookies = '';
|
|
127
139
|
|
|
128
140
|
// proxy middleware options
|
|
129
141
|
const optionsAlexa = {
|
|
@@ -139,12 +151,12 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
139
151
|
onProxyRes: onProxyRes,
|
|
140
152
|
onProxyReq: onProxyReq,
|
|
141
153
|
headers: {
|
|
142
|
-
'user-agent':
|
|
154
|
+
'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 PitanguiBridge/2.2.345247.0-[HARDWARE=iPhone10_4][SOFTWARE=13.5.1]',
|
|
143
155
|
'accept-language': _options.acceptLanguage,
|
|
144
156
|
'authority': `www.${_options.baseAmazonPage}`
|
|
145
157
|
},
|
|
146
158
|
cookieDomainRewrite: { // enhanced below
|
|
147
|
-
|
|
159
|
+
'*': ''
|
|
148
160
|
}
|
|
149
161
|
};
|
|
150
162
|
optionsAlexa.pathRewrite[`^/www.${_options.baseAmazonPage}`] = '';
|
|
@@ -178,7 +190,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
178
190
|
}
|
|
179
191
|
}
|
|
180
192
|
if (url === '/') { // initial redirect
|
|
181
|
-
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=
|
|
193
|
+
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}`;
|
|
182
194
|
_options.logger && _options.logger('Alexa-Cookie: Initial Page Request: ' + returnedInitUrl);
|
|
183
195
|
return returnedInitUrl;
|
|
184
196
|
}
|
|
@@ -203,8 +215,8 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
203
215
|
|
|
204
216
|
function replaceHosts(data) {
|
|
205
217
|
//const dataOrig = data;
|
|
206
|
-
const amazonRegex = new RegExp(`https?://www.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g,
|
|
207
|
-
const alexaRegex = new RegExp(`https?://alexa.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g,
|
|
218
|
+
const amazonRegex = new RegExp(`https?://www.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g, '\\.'), 'g');
|
|
219
|
+
const alexaRegex = new RegExp(`https?://alexa.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g, '\\.'), 'g');
|
|
208
220
|
data = data.replace(///g, '/');
|
|
209
221
|
data = data.replace(amazonRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`);
|
|
210
222
|
data = data.replace(alexaRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`);
|
|
@@ -213,8 +225,8 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
213
225
|
}
|
|
214
226
|
|
|
215
227
|
function replaceHostsBack(data) {
|
|
216
|
-
const amazonRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`.replace(/\./g,
|
|
217
|
-
const alexaRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`.replace(/\./g,
|
|
228
|
+
const amazonRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`.replace(/\./g, '\\.'), 'g');
|
|
229
|
+
const alexaRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`.replace(/\./g, '\\.'), 'g');
|
|
218
230
|
data = data.replace(amazonRegex, `https://www.${_options.baseAmazonPage}/`);
|
|
219
231
|
data = data.replace(alexaRegex, `https://alexa.${_options.baseAmazonPage}/`);
|
|
220
232
|
if (data === `http://${_options.proxyOwnIp}:${_options.proxyPort}/`) {
|
|
@@ -235,9 +247,9 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
235
247
|
_options.logger && _options.logger('Alexa-Cookie: Headers: ' + JSON.stringify(proxyReq.getHeaders()));
|
|
236
248
|
let reqCookie = proxyReq.getHeader('cookie');
|
|
237
249
|
if (reqCookie === undefined) {
|
|
238
|
-
reqCookie =
|
|
250
|
+
reqCookie = '';
|
|
239
251
|
}
|
|
240
|
-
for (
|
|
252
|
+
for (const cookie in initialCookies) {
|
|
241
253
|
if (!initialCookies.hasOwnProperty(cookie)) continue;
|
|
242
254
|
if (!reqCookie.includes(cookie + '=')) {
|
|
243
255
|
reqCookie += '; ' + cookie + '=' + initialCookies[cookie];
|
|
@@ -258,7 +270,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
258
270
|
let modified = false;
|
|
259
271
|
if (req.method === 'POST') {
|
|
260
272
|
if (typeof proxyReq.getHeader === 'function' && proxyReq.getHeader('referer')) {
|
|
261
|
-
|
|
273
|
+
const fixedReferer = replaceHostsBack(proxyReq.getHeader('referer'));
|
|
262
274
|
if (fixedReferer ) {
|
|
263
275
|
proxyReq.setHeader('referer', fixedReferer);
|
|
264
276
|
_options.logger && _options.logger('Alexa-Cookie: Modify headers: Changed Referer: ' + fixedReferer);
|
|
@@ -318,11 +330,12 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
318
330
|
_options.logger && _options.logger('Alexa-Cookie: Proxy catched parameters: ' + JSON.stringify(queryParams));
|
|
319
331
|
|
|
320
332
|
callbackCookie && callbackCookie(null, {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
333
|
+
'loginCookie': proxyCookies,
|
|
334
|
+
'authorization_code': queryParams['openid.oa2.authorization_code'],
|
|
335
|
+
'frc': initialCookies.frc,
|
|
336
|
+
'map-md': initialCookies['map-md'],
|
|
337
|
+
'deviceId': deviceId,
|
|
338
|
+
'verifier': code_verifier
|
|
326
339
|
});
|
|
327
340
|
return;
|
|
328
341
|
}
|
|
@@ -363,7 +376,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
|
|
|
363
376
|
app.get('/cookie-success', function(req, res) {
|
|
364
377
|
res.send('<b>Amazon Alexa Cookie successfully retrieved. You can close the browser.</b>');
|
|
365
378
|
});
|
|
366
|
-
|
|
379
|
+
const server = app.listen(_options.proxyPort, _options.proxyListenBind, function() {
|
|
367
380
|
_options.logger && _options.logger('Alexa-Cookie: Proxy-Server listening on port ' + server.address().port);
|
|
368
381
|
callbackListening && callbackListening(server);
|
|
369
382
|
callbackListening = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alexa-cookie2",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Generate Cookie and CSRF for Alexa Remote",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Apollon77",
|
|
@@ -21,15 +21,17 @@
|
|
|
21
21
|
"layla.amazon.de"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"cookie": "^0.
|
|
25
|
-
"express": "^4.
|
|
26
|
-
"http-proxy-middleware": "^
|
|
24
|
+
"cookie": "^0.5.0",
|
|
25
|
+
"express": "^4.18.1",
|
|
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": "^
|
|
32
|
+
"@alcalzone/release-script": "^3.5.9",
|
|
33
|
+
"@alcalzone/release-script-plugin-license": "^3.5.9",
|
|
34
|
+
"eslint": "^8.18.0"
|
|
33
35
|
},
|
|
34
36
|
"repository": {
|
|
35
37
|
"type": "git",
|
|
@@ -41,6 +43,9 @@
|
|
|
41
43
|
"scripts": {
|
|
42
44
|
"release": "release-script"
|
|
43
45
|
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=12.0.0"
|
|
48
|
+
},
|
|
44
49
|
"main": "alexa-cookie.js",
|
|
45
50
|
"readmeFilename": "readme.md"
|
|
46
51
|
}
|
package/.commitmessage
DELETED