homebridge-melcloud-control 4.0.0-beta.407 → 4.0.0-beta.408

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "displayName": "MELCloud Control",
3
3
  "name": "homebridge-melcloud-control",
4
- "version": "4.0.0-beta.407",
4
+ "version": "4.0.0-beta.408",
5
5
  "description": "Homebridge plugin to control Mitsubishi Air Conditioner, Heat Pump and Energy Recovery Ventilation.",
6
6
  "license": "MIT",
7
7
  "author": "grzegorz914",
package/src/melcloud.js CHANGED
@@ -291,7 +291,7 @@ class MelCloud extends EventEmitter {
291
291
 
292
292
  //const { codeVerifier, url } = await melCloudHomeToken.buildAuthorizeUrl();
293
293
  //const code = await melCloudHomeToken.loginToMelCloudHome(url);
294
- const token = await melCloudHomeToken.getTokens(this.user, this.passwd);
294
+ const token = await melCloudHomeToken.login(this.user, this.passwd);
295
295
 
296
296
  const accountInfo = { ContextKey: token, UseFahrenheit: false };
297
297
  this.contextKey = code;
@@ -19,7 +19,7 @@ class MelCloudHomeToken extends EventEmitter {
19
19
  this.http = wrapper(axios.create({ jar: this.cookieJar }));
20
20
  }
21
21
 
22
- // --- Utility: generate PKCE pair ---
22
+ // --- PKCE ---
23
23
  _generatePkcePair() {
24
24
  const verifier = crypto.randomBytes(32).toString('base64url');
25
25
  const challenge = crypto
@@ -29,14 +29,19 @@ class MelCloudHomeToken extends EventEmitter {
29
29
  return { verifier, challenge };
30
30
  }
31
31
 
32
- // --- Utility: extract CSRF token from HTML ---
33
- _extractCsrf(html) {
32
+ // --- Extract Cognito hidden form fields ---
33
+ _extractLoginForm(html) {
34
34
  if (typeof html !== 'string') return null;
35
- const match = html.match(/name=['"]_csrf['"][^>]*value=['"]([^'"]+)['"]/i);
36
- return match ? match[1] : null;
35
+
36
+ const state = html.match(/name=['"]state['"][^>]*value=['"]([^'"]+)['"]/i)?.[1];
37
+ const clientId = html.match(/name=['"]client_id['"][^>]*value=['"]([^'"]+)['"]/i)?.[1];
38
+ const redirectUri = html.match(/name=['"]redirect_uri['"][^>]*value=['"]([^'"]+)['"]/i)?.[1];
39
+
40
+ if (!state || !clientId || !redirectUri) return null;
41
+ return { state, clientId, redirectUri };
37
42
  }
38
43
 
39
- // --- GET request with redirect + cookie management ---
44
+ // --- GET helper with manual redirect handling ---
40
45
  async _get(url, depth = 0) {
41
46
  const cookies = await this.cookieJar.getCookieString(url);
42
47
  const resp = await this.http.get(url, {
@@ -61,7 +66,7 @@ class MelCloudHomeToken extends EventEmitter {
61
66
  );
62
67
  }
63
68
 
64
- // Follow redirect manually
69
+ // Handle redirect
65
70
  if (
66
71
  resp.status >= 300 &&
67
72
  resp.status < 400 &&
@@ -76,7 +81,7 @@ class MelCloudHomeToken extends EventEmitter {
76
81
  return resp;
77
82
  }
78
83
 
79
- // --- POST request helper ---
84
+ // --- POST helper ---
80
85
  async _post(url, data, headers = {}) {
81
86
  const cookies = await this.cookieJar.getCookieString(url);
82
87
  const resp = await this.http.post(url, data, {
@@ -103,11 +108,10 @@ class MelCloudHomeToken extends EventEmitter {
103
108
  return resp;
104
109
  }
105
110
 
106
- // --- Main login flow ---
107
- async getTokens(email, password) {
111
+ // --- MAIN LOGIN FLOW ---
112
+ async login(email, password) {
108
113
  const { verifier, challenge } = this._generatePkcePair();
109
114
 
110
- // Build authorize URL
111
115
  const authUrl =
112
116
  `${AUTH_BASE}/oauth2/authorize?` +
113
117
  new URLSearchParams({
@@ -119,7 +123,7 @@ class MelCloudHomeToken extends EventEmitter {
119
123
  code_challenge_method: 'S256',
120
124
  });
121
125
 
122
- console.log(`[OAuth] Opening login page...`);
126
+ console.log('[OAuth] Opening login page...');
123
127
  let resp = await this._get(authUrl);
124
128
  if (resp.status >= 300 && resp.status < 400) {
125
129
  const redirectUrl = new URL(resp.headers.location, AUTH_BASE).href;
@@ -127,47 +131,48 @@ class MelCloudHomeToken extends EventEmitter {
127
131
  resp = await this._get(redirectUrl);
128
132
  }
129
133
 
130
- // --- Extract CSRF token ---
131
- const csrf = this._extractCsrf(resp.data);
132
- if (!csrf) {
133
- console.error('[OAuth] ❌ Cannot find CSRF token!');
134
- console.log(resp.data.substring(0, 400));
135
- throw new Error('Cannot find CSRF token');
134
+ // Extract login form fields
135
+ const form = this._extractLoginForm(resp.data);
136
+ if (!form) {
137
+ console.error('[OAuth] ❌ Cannot find Cognito login form fields!');
138
+ console.log(resp.data.substring(0, 500));
139
+ throw new Error('Cannot find login form');
136
140
  }
137
141
 
138
- console.log(`[OAuth] Found CSRF token: ${csrf}`);
142
+ console.log(`[OAuth] Found form state: ${form.state.substring(0, 12)}...`);
139
143
 
140
- // --- Submit login form ---
141
144
  const loginUrl = resp.request.res.responseUrl || `${AUTH_BASE}/login`;
142
145
  const formData = new URLSearchParams({
143
- _csrf: csrf,
146
+ state: form.state,
147
+ client_id: form.clientId,
148
+ redirect_uri: form.redirectUri,
144
149
  username: email,
145
150
  password: password,
146
151
  });
147
152
 
148
- console.log(`[OAuth] Submitting login form...`);
153
+ console.log('[OAuth] Submitting Cognito login...');
149
154
  const loginResp = await this._post(loginUrl, formData.toString());
150
155
 
156
+ // Cognito redirects with ?code=
151
157
  if (loginResp.status >= 300 && loginResp.status < 400) {
152
158
  const redirectUrl = new URL(loginResp.headers.location, AUTH_BASE).href;
153
159
  console.log(`[OAuth] Login redirect → ${redirectUrl}`);
154
160
 
155
- // Extract code from redirect URL
156
161
  const codeMatch = redirectUrl.match(/[?&]code=([^&]+)/);
157
162
  if (!codeMatch) throw new Error('No code found after login redirect');
163
+
158
164
  const code = codeMatch[1];
159
165
  console.log(`[OAuth] ✅ Got authorization code: ${code}`);
160
166
 
161
- // Exchange code for token
162
167
  return this.exchangeToken(code, verifier);
163
168
  }
164
169
 
165
- console.error('[OAuth] ❌ Unexpected status on login:', loginResp.status);
170
+ console.error('[OAuth] ❌ Unexpected login status:', loginResp.status);
166
171
  console.log(loginResp.data.substring(0, 400));
167
- throw new Error('Unexpected status on login page');
172
+ throw new Error('Unexpected login response');
168
173
  }
169
174
 
170
- // --- Exchange code for access token ---
175
+ // --- Exchange code for token ---
171
176
  async exchangeToken(code, verifier) {
172
177
  console.log('[OAuth] Exchanging code for token...');
173
178
 
@@ -192,12 +197,14 @@ class MelCloudHomeToken extends EventEmitter {
192
197
  throw new Error('Token exchange failed');
193
198
  }
194
199
 
195
- console.log('[OAuth] ✅ Token response received');
200
+ console.log('[OAuth] ✅ Token received successfully');
196
201
  return resp.data;
197
202
  }
198
203
  }
199
204
 
200
205
 
206
+
207
+
201
208
  export default MelCloudHomeToken;
202
209
 
203
210