alexa-cookie2 4.0.1 → 4.1.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/.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
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "plugins": ["license"]
3
+ }
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018-2021 Apollon77 <ingo@fischer-ka.de>
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
@@ -6,6 +6,10 @@
6
6
 
7
7
  Library to generate/retrieve a cookie including a csrf for alexa remote
8
8
 
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.**
12
+
9
13
  ## Description
10
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.
11
15
  If the automatic authentication fails (which is more common case in the meantime because of security checks from amazon like a needed Captcha or because you enabled two factor authentication) the library can also setup a proxy server to allow the manual login and will catch the cookie by itself. Using this proxy you can enter needed 2FA codes or solve captchas and still do not need to trick around to get the cookie.
@@ -45,6 +49,16 @@ Partly based on [Amazon Alexa Remote Control](http://blog.loetzimmer.de/2017/10/
45
49
  Thank you for that work.
46
50
 
47
51
  ## Changelog:
52
+ ### 4.1.0 (2022-07-18)
53
+ * (Apollon77) Allow to overwrite the used App-Name for the Amazon App Registration.
54
+ * (Apollon77) Include the used app name also in the response
55
+
56
+ ### 4.0.3 (2022-07-06)
57
+ * (Apollon77) Update some request meta data to match current Alexa Apps
58
+
59
+ ### 4.0.2 (2022-06-30)
60
+ * (Apollon77) Prevent potential crash cases
61
+
48
62
  ### 4.0.1 (2021-10-11)
49
63
  * (Apollon77) Adjust call headers
50
64
 
@@ -140,4 +154,4 @@ Thank you for that work.
140
154
  * (Apollon77) 0.1.1: update to get it working again and sync to [alexa-remote-control](https://github.com/thorsten-gehrig/alexa-remote-control)
141
155
 
142
156
  ### 0.0.x
143
- * Versions by soef
157
+ * Versions by soef
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
@@ -25,6 +18,10 @@ const defaultUserAgentLinux = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.3
25
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';
26
19
  const defaultAcceptLanguage = 'de-DE';
27
20
 
21
+ const apiCallVersion = '2.2.483723.0';
22
+ const apiCallUserAgent = 'AmazonWebView/Amazon Alexa/2.2.483723.0/iOS/15.5/iPhone';
23
+ const defaultAppName = 'ioBroker Alexa2';
24
+
28
25
  const csrfOptions = [
29
26
  '/api/language',
30
27
  '/spa/index.html',
@@ -57,8 +54,7 @@ function AlexaCookie() {
57
54
  }
58
55
  }
59
56
  Cookie = '';
60
- for (let name in cookies) {
61
- if (!cookies.hasOwnProperty(name)) continue;
57
+ for (const name of Object.keys(cookies)) {
62
58
  Cookie += name + '=' + cookies[name] + '; ';
63
59
  }
64
60
  Cookie = Cookie.replace(/[; ]*$/, '');
@@ -83,14 +79,14 @@ function AlexaCookie() {
83
79
  removeContentLength = true;
84
80
  }
85
81
 
86
- let req = https.request(options, (res) => {
87
- let body = "";
82
+ const req = https.request(options, (res) => {
83
+ let body = '';
88
84
  info.requests.push({options: options, response: res});
89
85
 
90
86
  if (options.followRedirects !== false && res.statusCode >= 300 && res.statusCode < 400) {
91
87
  _options.logger && _options.logger('Alexa-Cookie: Response (' + res.statusCode + ')' + (res.headers.location ? ' - Redirect to ' + res.headers.location : ''));
92
88
  //options.url = res.headers.location;
93
- let u = url.parse(res.headers.location);
89
+ const u = url.parse(res.headers.location);
94
90
  if (u.host) options.host = u.host;
95
91
  options.path = u.path;
96
92
  options.method = 'GET';
@@ -127,11 +123,11 @@ function AlexaCookie() {
127
123
  const getFields = body => {
128
124
  body = body.replace(/[\n\r]/g, ' ');
129
125
  let re = /^.*?("hidden"\s*name=".*$)/;
130
- let ar = re.exec(body);
126
+ const ar = re.exec(body);
131
127
  if (!ar || ar.length < 2) return {};
132
128
  let h;
133
129
  re = /.*?name="([^"]+)"[\s^\s]*value="([^"]+).*?"/g;
134
- let data = {};
130
+ const data = {};
135
131
  while ((h = re.exec(ar[1])) !== null) {
136
132
  if (h[1] !== 'rememberMe') {
137
133
  data[h[1]] = h[2];
@@ -149,6 +145,9 @@ function AlexaCookie() {
149
145
  _options.baseAmazonPage = _options.baseAmazonPage || 'amazon.com';
150
146
  _options.logger && _options.logger('Alexa-Cookie: Use as Base-Amazon-URL: ' + _options.baseAmazonPage);
151
147
 
148
+ _options.deviceAppName = _options.deviceAppName || defaultAppName;
149
+ _options.logger && _options.logger('Alexa-Cookie: Use as Device-App-Name: ' + _options.deviceAppName);
150
+
152
151
  if (!_options.baseAmazonPageHandle && _options.baseAmazonPageHandle !== '') {
153
152
  const amazonDomain = _options.baseAmazonPage.substr(_options.baseAmazonPage.lastIndexOf('.') + 1);
154
153
  if (amazonDomain === 'jp') {
@@ -164,7 +163,7 @@ function AlexaCookie() {
164
163
  }
165
164
 
166
165
  if (!_options.userAgent) {
167
- let platform = os.platform();
166
+ const platform = os.platform();
168
167
  if (platform === 'win32') {
169
168
  _options.userAgent = defaultUserAgent;
170
169
  }
@@ -205,13 +204,13 @@ function AlexaCookie() {
205
204
 
206
205
  function csrfTry() {
207
206
  const path = csrfUrls.shift();
208
- let options = {
207
+ const options = {
209
208
  'host': 'alexa.' + _options.amazonPage,
210
209
  'path': path,
211
210
  'method': 'GET',
212
211
  'headers': {
213
212
  'DNT': '1',
214
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
213
+ 'User-Agent': _options.userAgent,
215
214
  'Connection': 'keep-alive',
216
215
  'Referer': 'https://alexa.' + _options.amazonPage + '/spa/index.html',
217
216
  'Cookie': cookie,
@@ -223,8 +222,8 @@ function AlexaCookie() {
223
222
  _options.logger && _options.logger('Alexa-Cookie: Step 4: get CSRF via ' + path);
224
223
  request(options, (error, response) => {
225
224
  cookie = addCookies(cookie, response ? response.headers : null);
226
- let ar = /csrf=([^;]+)/.exec(cookie);
227
- let csrf = ar ? ar[1] : undefined;
225
+ const ar = /csrf=([^;]+)/.exec(cookie);
226
+ const csrf = ar ? ar[1] : undefined;
228
227
  _options.logger && _options.logger('Alexa-Cookie: Result: csrf=' + csrf + ', Cookie=' + cookie);
229
228
  if (!csrf && csrfUrls.length) {
230
229
  csrfTry();
@@ -268,7 +267,7 @@ function AlexaCookie() {
268
267
 
269
268
  if (!_options.proxyOnly) {
270
269
  // get first cookie and write redirection target into referer
271
- let options = {
270
+ const options = {
272
271
  host: 'alexa.' + _options.amazonPage,
273
272
  path: '',
274
273
  method: 'GET',
@@ -288,10 +287,10 @@ function AlexaCookie() {
288
287
  return;
289
288
  }
290
289
 
291
- let lastRequestOptions = info.requests[info.requests.length - 1].options;
290
+ const lastRequestOptions = info.requests[info.requests.length - 1].options;
292
291
  // login empty to generate session
293
292
  Cookie = addCookies(Cookie, response.headers);
294
- let options = {
293
+ const options = {
295
294
  host: 'www.' + _options.amazonPage,
296
295
  path: '/ap/signin',
297
296
  method: 'POST',
@@ -322,7 +321,7 @@ function AlexaCookie() {
322
321
  options.path = '/ap/signin';
323
322
  options.method = 'POST';
324
323
  options.headers.Cookie = Cookie = addCookies(Cookie, response.headers);
325
- let ar = options.headers.Cookie.match(/session-id=([^;]+)/);
324
+ const ar = options.headers.Cookie.match(/session-id=([^;]+)/);
326
325
  options.headers.Referer = `https://www.${_options.amazonPage}/ap/signin/${ar[1]}`;
327
326
  options.body = getFields(body);
328
327
  options.body.email = email || '';
@@ -336,7 +335,7 @@ function AlexaCookie() {
336
335
  return;
337
336
  }
338
337
 
339
- let lastRequestOptions = info.requests[info.requests.length - 1].options;
338
+ const lastRequestOptions = info.requests[info.requests.length - 1].options;
340
339
 
341
340
  // check whether the login has been successful or exit otherwise
342
341
  if (!lastRequestOptions.host.startsWith('alexa') || !lastRequestOptions.path.endsWith('.html')) {
@@ -397,10 +396,15 @@ function AlexaCookie() {
397
396
  }
398
397
  };
399
398
 
399
+ this.getDeviceAppName = () => {
400
+ return _options.deviceAppName || defaultAppName;
401
+ };
400
402
 
401
403
  const handleTokenRegistration = (_options, loginData, callback) => {
402
404
  _options.logger && _options.logger('Handle token registration Start: ' + JSON.stringify(loginData));
403
405
 
406
+ loginData.deviceAppName = _options.deviceAppName;
407
+
404
408
  let deviceSerial;
405
409
  if (!_options.formerRegistrationData || !_options.formerRegistrationData.deviceSerial) {
406
410
  const deviceSerialBuffer = Buffer.alloc(16);
@@ -422,69 +426,68 @@ function AlexaCookie() {
422
426
  */
423
427
 
424
428
  const registerData = {
425
- "requested_extensions": [
426
- "device_info",
427
- "customer_info"
429
+ 'requested_extensions': [
430
+ 'device_info',
431
+ 'customer_info'
428
432
  ],
429
- "cookies": {
430
- "website_cookies": [
433
+ 'cookies': {
434
+ 'website_cookies': [
431
435
  /*{
432
436
  "Value": cookies["session-id-time"],
433
437
  "Name": "session-id-time"
434
438
  }*/
435
439
  ],
436
- "domain": "." + _options.baseAmazonPage
440
+ 'domain': '.' + _options.baseAmazonPage
437
441
  },
438
- "registration_data": {
439
- "domain": "Device",
440
- "app_version": "2.2.443692.0",
441
- "device_type": "A2IVLV5VM2W81",
442
- "device_name": "%FIRST_NAME%\u0027s%DUPE_STRATEGY_1ST%ioBroker Alexa2",
443
- "os_version": "14.8",
444
- "device_serial": deviceSerial,
445
- "device_model": "iPhone",
446
- "app_name": "ioBroker Alexa2",
447
- "software_version": "1"
442
+ 'registration_data': {
443
+ 'domain': 'Device',
444
+ 'app_version': apiCallVersion,
445
+ 'device_type': 'A2IVLV5VM2W81',
446
+ 'device_name': '%FIRST_NAME%\u0027s%DUPE_STRATEGY_1ST%ioBroker Alexa2',
447
+ 'os_version': '15.5',
448
+ 'device_serial': deviceSerial,
449
+ 'device_model': 'iPhone',
450
+ 'app_name': _options.deviceAppName,
451
+ 'software_version': '1'
448
452
  },
449
- "auth_data": {
453
+ 'auth_data': {
450
454
  // Filled below
451
455
  },
452
- "user_context_map": {
453
- "frc": cookies.frc
456
+ 'user_context_map': {
457
+ 'frc': cookies.frc
454
458
  },
455
- "requested_token_type": [
456
- "bearer",
457
- "mac_dms",
458
- "website_cookies"
459
+ 'requested_token_type': [
460
+ 'bearer',
461
+ 'mac_dms',
462
+ 'website_cookies'
459
463
  ]
460
464
  };
461
465
  if (loginData.accessToken) {
462
466
  registerData.auth_data = {
463
- "access_token": loginData.accessToken
467
+ 'access_token': loginData.accessToken
464
468
  };
465
469
  } else if (loginData.authorization_code && loginData.verifier) {
466
470
  registerData.auth_data = {
467
- "client_id" : loginData.deviceId,
468
- "authorization_code" : loginData.authorization_code,
469
- "code_verifier" : loginData.verifier,
470
- "code_algorithm" : "SHA-256",
471
- "client_domain" : "DeviceLegacy"
471
+ 'client_id' : loginData.deviceId,
472
+ 'authorization_code' : loginData.authorization_code,
473
+ 'code_verifier' : loginData.verifier,
474
+ 'code_algorithm' : 'SHA-256',
475
+ 'client_domain' : 'DeviceLegacy'
472
476
  };
473
477
  }
474
- for (let key in cookies) {
475
- if (!cookies.hasOwnProperty(key)) continue;
478
+ for (const key of Object.keys(cookies)) {
476
479
  registerData.cookies.website_cookies.push({
477
- "Value": cookies[key],
478
- "Name": key
480
+ 'Value': cookies[key],
481
+ 'Name': key
479
482
  });
480
483
  }
481
484
 
482
- let options = {
485
+ const options = {
483
486
  host: 'api.' + _options.baseAmazonPage,
484
487
  path: '/auth/register',
485
488
  method: 'POST',
486
489
  headers: {
487
- 'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
490
+ 'User-Agent': apiCallUserAgent,
488
491
  'Accept-Language': _options.acceptLanguage,
489
492
  'Accept-Charset': 'utf-8',
490
493
  'Connection': 'keep-alive',
@@ -524,12 +527,12 @@ function AlexaCookie() {
524
527
  Get Amazon Marketplace Country
525
528
  */
526
529
 
527
- let options = {
530
+ const options = {
528
531
  host: 'alexa.' + _options.baseAmazonPage,
529
- path: '/api/users/me?platform=ios&version=2.2.443692.0',
532
+ path: '/api/users/me?platform=ios&version=' + apiCallVersion,
530
533
  method: 'GET',
531
534
  headers: {
532
- 'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
535
+ 'User-Agent': apiCallUserAgent,
533
536
  'Accept-Language': _options.acceptLanguage,
534
537
  'Accept-Charset': 'utf-8',
535
538
  'Connection': 'keep-alive',
@@ -560,7 +563,7 @@ function AlexaCookie() {
560
563
  } else if (error && (!_options || !_options.amazonPage)) {
561
564
  callback && callback(error, null);
562
565
  return;
563
- } else if (error && !_options.formerRegistrationData.amazonPage && _options.amazonPage) {
566
+ } else if (error && (!_options.formerRegistrationData || !_options.formerRegistrationData.amazonPage) && _options.amazonPage) {
564
567
  _options.logger && _options.logger('Continue with externally set amazonPage: ' + _options.amazonPage);
565
568
  } else if (error) {
566
569
  _options.logger && _options.logger('Ignore error while getting user data and amazonPage because previously set amazonPage is available');
@@ -600,7 +603,7 @@ function AlexaCookie() {
600
603
 
601
604
  const exchangeParams = {
602
605
  'di.os.name': 'iOS',
603
- 'app_version': '2.2.443692.0',
606
+ 'app_version': apiCallVersion,
604
607
  'domain': '.' + amazonPage,
605
608
  'source_token': refreshToken,
606
609
  'requested_token_type': 'auth_cookies',
@@ -611,12 +614,12 @@ function AlexaCookie() {
611
614
  'app_name': 'Amazon Alexa',
612
615
  'di.os.version': '11.4.1'
613
616
  };
614
- let options = {
617
+ const options = {
615
618
  host: 'www.' + amazonPage,
616
619
  path: '/ap/exchangetoken',
617
620
  method: 'POST',
618
621
  headers: {
619
- 'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
622
+ 'User-Agent': apiCallUserAgent,
620
623
  'Accept-Language': _options.acceptLanguage,
621
624
  'Accept-Charset': 'utf-8',
622
625
  'Connection': 'keep-alive',
@@ -665,8 +668,7 @@ function AlexaCookie() {
665
668
 
666
669
  });
667
670
  let localCookie = '';
668
- for (let name in cookies) {
669
- if (!cookies.hasOwnProperty(name)) continue;
671
+ for (const name of Object.keys(cookies)) {
670
672
  localCookie += name + '=' + cookies[name] + '; ';
671
673
  }
672
674
  localCookie = localCookie.replace(/[; ]*$/, '');
@@ -693,26 +695,26 @@ function AlexaCookie() {
693
695
  initConfig();
694
696
 
695
697
  const refreshData = {
696
- "app_name": "ioBroker Alexa2",
697
- "app_version": "2.2.443692.0",
698
- "di.sdk.version": "6.10.0",
699
- "source_token": _options.formerRegistrationData.refreshToken,
700
- "package_name": "com.amazon.echo",
701
- "di.hw.version": "iPhone",
702
- "platform": "iOS",
703
- "requested_token_type": "access_token",
704
- "source_token_type": "refresh_token",
705
- "di.os.name": "iOS",
706
- "di.os.version": "14.8",
707
- "current_version": "6.10.0"
698
+ 'app_name': 'ioBroker Alexa2',
699
+ 'app_version': apiCallVersion,
700
+ 'di.sdk.version': '6.10.0',
701
+ 'source_token': _options.formerRegistrationData.refreshToken,
702
+ 'package_name': 'com.amazon.echo',
703
+ 'di.hw.version': 'iPhone',
704
+ 'platform': 'iOS',
705
+ 'requested_token_type': 'access_token',
706
+ 'source_token_type': 'refresh_token',
707
+ 'di.os.name': 'iOS',
708
+ 'di.os.version': '14.8',
709
+ 'current_version': '6.10.0'
708
710
  };
709
711
 
710
- let options = {
712
+ const options = {
711
713
  host: 'api.' + _options.baseAmazonPage,
712
714
  path: '/auth/token',
713
715
  method: 'POST',
714
716
  headers: {
715
- 'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.443692.0/iOS/14.8/iPhone',
717
+ 'User-Agent': apiCallUserAgent,
716
718
  'Accept-Language': _options.acceptLanguage,
717
719
  'Accept-Charset': 'utf-8',
718
720
  'Connection': 'keep-alive',
@@ -16,9 +16,10 @@ const config = {
16
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
17
  proxyPort: 3456, // optional: use this port for the proxy, default is 0 means random port is selected
18
18
  proxyListenBind: '0.0.0.0',// optional: set this to bind the proxy to a special IP, default is '0.0.0.0'
19
- proxyLogLevel: 'info', // optional: Loglevel of Proxy, default 'warn'
19
+ proxyLogLevel: 'info', // optional: Loglevel of Proxy, default 'warn'
20
20
  baseAmazonPage: 'amazon.com', // optional: Change the Proxy Amazon Page - all "western countries" directly use amazon.com! Change to amazon.co.jp for Japan
21
21
  amazonPageProxyLanguage: 'de_DE', // optional: language to be used for the Amazon Sign-in page the proxy calls. default is "de_DE")
22
+ deviceAppName: '...', // optional: name of the device app name which will be registered with Amazon, leave empty to use a default one
22
23
  formerDataStorePath: '...', // optional: overwrite path where some of the formerRegistrationData are persisted to optimize against Amazon security measures
23
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
25
  };
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
- "use strict";
6
+ 'use strict';
7
7
 
8
8
  const modifyResponse = require('http-proxy-response-rewrite');
9
9
  const express = require('express');
@@ -27,8 +27,7 @@ function addCookies(Cookie, headers) {
27
27
  }
28
28
  }
29
29
  Cookie = '';
30
- for (let name in cookies) {
31
- if (!cookies.hasOwnProperty(name)) continue;
30
+ for (const name of Object.keys(cookies)) {
32
31
  Cookie += name + '=' + cookies[name] + '; ';
33
32
  }
34
33
  Cookie = Cookie.replace(/[; ]*$/, '');
@@ -90,7 +89,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
90
89
  initialCookies.frc = _options.formerRegistrationData.frc;
91
90
  }
92
91
 
93
- if (!_options.formerRegistrationData || !_options.formerRegistrationData["map-md"]) {
92
+ if (!_options.formerRegistrationData || !_options.formerRegistrationData['map-md']) {
94
93
  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
94
  }
96
95
  else {
@@ -123,19 +122,19 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
123
122
  // ignore
124
123
  }
125
124
 
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));
125
+ function base64URLEncode(str) {
126
+ return str.toString('base64')
127
+ .replace(/\+/g, '-')
128
+ .replace(/\//g, '_')
129
+ .replace(/=/g, '');
130
+ }
131
+ function sha256(buffer) {
132
+ return crypto.createHash('sha256').update(buffer).digest();
133
+ }
134
+ const code_verifier = base64URLEncode(crypto.randomBytes(32));
135
+ const code_challenge = base64URLEncode(sha256(code_verifier));
137
136
 
138
- let proxyCookies = "";
137
+ let proxyCookies = '';
139
138
 
140
139
  // proxy middleware options
141
140
  const optionsAlexa = {
@@ -151,19 +150,19 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
151
150
  onProxyRes: onProxyRes,
152
151
  onProxyReq: onProxyReq,
153
152
  headers: {
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]",
153
+ '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]',
155
154
  'accept-language': _options.acceptLanguage,
156
155
  'authority': `www.${_options.baseAmazonPage}`
157
156
  },
158
157
  cookieDomainRewrite: { // enhanced below
159
- "*": ""
158
+ '*': ''
160
159
  }
161
160
  };
162
161
  optionsAlexa.pathRewrite[`^/www.${_options.baseAmazonPage}`] = '';
163
162
  optionsAlexa.pathRewrite[`^/alexa.${_options.baseAmazonPage}`] = '';
164
163
  optionsAlexa.cookieDomainRewrite[`.${_options.baseAmazonPage}`] = _options.proxyOwnIp;
165
164
  optionsAlexa.cookieDomainRewrite[_options.baseAmazonPage] = _options.proxyOwnIp;
166
- if (_options.logger) optionsAlexa.logProvider = function logProvider(_provider) {
165
+ if (_options.logger) optionsAlexa.logProvider = function logProvider() {
167
166
  return {
168
167
  log: _options.logger.log || _options.logger,
169
168
  debug: _options.logger.debug || _options.logger,
@@ -190,7 +189,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
190
189
  }
191
190
  }
192
191
  if (url === '/') { // initial redirect
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}`;
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}`;
194
193
  _options.logger && _options.logger('Alexa-Cookie: Initial Page Request: ' + returnedInitUrl);
195
194
  return returnedInitUrl;
196
195
  }
@@ -215,8 +214,8 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
215
214
 
216
215
  function replaceHosts(data) {
217
216
  //const dataOrig = data;
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');
217
+ const amazonRegex = new RegExp(`https?://www.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g, '\\.'), 'g');
218
+ const alexaRegex = new RegExp(`https?://alexa.${_options.baseAmazonPage}:?[0-9]*/`.replace(/\./g, '\\.'), 'g');
220
219
  data = data.replace(/&#x2F;/g, '/');
221
220
  data = data.replace(amazonRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`);
222
221
  data = data.replace(alexaRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`);
@@ -225,8 +224,8 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
225
224
  }
226
225
 
227
226
  function replaceHostsBack(data) {
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');
227
+ const amazonRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.${_options.baseAmazonPage}/`.replace(/\./g, '\\.'), 'g');
228
+ const alexaRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.${_options.baseAmazonPage}/`.replace(/\./g, '\\.'), 'g');
230
229
  data = data.replace(amazonRegex, `https://www.${_options.baseAmazonPage}/`);
231
230
  data = data.replace(alexaRegex, `https://alexa.${_options.baseAmazonPage}/`);
232
231
  if (data === `http://${_options.proxyOwnIp}:${_options.proxyPort}/`) {
@@ -235,7 +234,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
235
234
  return data;
236
235
  }
237
236
 
238
- function onProxyReq(proxyReq, req, _res) {
237
+ function onProxyReq(proxyReq, req/*, _res*/) {
239
238
  const url = req.originalUrl || req.url;
240
239
  if (url.endsWith('.ico') || url.endsWith('.js') || url.endsWith('.ttf') || url.endsWith('.svg') || url.endsWith('.png') || url.endsWith('.appcache')) return;
241
240
  //if (url.startsWith('/ap/uedata')) return;
@@ -247,10 +246,9 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
247
246
  _options.logger && _options.logger('Alexa-Cookie: Headers: ' + JSON.stringify(proxyReq.getHeaders()));
248
247
  let reqCookie = proxyReq.getHeader('cookie');
249
248
  if (reqCookie === undefined) {
250
- reqCookie = "";
249
+ reqCookie = '';
251
250
  }
252
- for (var cookie in initialCookies) {
253
- if (!initialCookies.hasOwnProperty(cookie)) continue;
251
+ for (const cookie of Object.keys(initialCookies)) {
254
252
  if (!reqCookie.includes(cookie + '=')) {
255
253
  reqCookie += '; ' + cookie + '=' + initialCookies[cookie];
256
254
  }
@@ -270,7 +268,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
270
268
  let modified = false;
271
269
  if (req.method === 'POST') {
272
270
  if (typeof proxyReq.getHeader === 'function' && proxyReq.getHeader('referer')) {
273
- let fixedReferer = replaceHostsBack(proxyReq.getHeader('referer'));
271
+ const fixedReferer = replaceHostsBack(proxyReq.getHeader('referer'));
274
272
  if (fixedReferer ) {
275
273
  proxyReq.setHeader('referer', fixedReferer);
276
274
  _options.logger && _options.logger('Alexa-Cookie: Modify headers: Changed Referer: ' + fixedReferer);
@@ -330,12 +328,12 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
330
328
  _options.logger && _options.logger('Alexa-Cookie: Proxy catched parameters: ' + JSON.stringify(queryParams));
331
329
 
332
330
  callbackCookie && callbackCookie(null, {
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
331
+ 'loginCookie': proxyCookies,
332
+ 'authorization_code': queryParams['openid.oa2.authorization_code'],
333
+ 'frc': initialCookies.frc,
334
+ 'map-md': initialCookies['map-md'],
335
+ 'deviceId': deviceId,
336
+ 'verifier': code_verifier
339
337
  });
340
338
  return;
341
339
  }
@@ -376,7 +374,7 @@ function initAmazonProxy(_options, callbackCookie, callbackListening) {
376
374
  app.get('/cookie-success', function(req, res) {
377
375
  res.send('<b>Amazon Alexa Cookie successfully retrieved. You can close the browser.</b>');
378
376
  });
379
- let server = app.listen(_options.proxyPort, _options.proxyListenBind, function() {
377
+ const server = app.listen(_options.proxyPort, _options.proxyListenBind, function() {
380
378
  _options.logger && _options.logger('Alexa-Cookie: Proxy-Server listening on port ' + server.address().port);
381
379
  callbackListening && callbackListening(server);
382
380
  callbackListening = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alexa-cookie2",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
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.4.1",
25
- "express": "^4.17.1",
26
- "http-proxy-middleware": "^2.0.1",
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": "^3.4.0"
32
+ "@alcalzone/release-script": "^3.5.9",
33
+ "@alcalzone/release-script-plugin-license": "^3.5.9",
34
+ "eslint": "^8.20.0"
33
35
  },
34
36
  "repository": {
35
37
  "type": "git",