n8n-nodes-jygse-vw-weconnect 0.1.4 → 0.1.6

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.
@@ -189,19 +189,24 @@ async function vwLogin(context, email, password) {
189
189
  encoding: 'text',
190
190
  returnFullResponse: true,
191
191
  ignoreHttpStatusErrors: true,
192
+ skipAutoFollowRedirects: true,
192
193
  });
193
194
  // Extract response body and check for redirect
194
195
  let htmlContent;
195
196
  let currentUrl = '';
197
+ let stateToken = '';
196
198
  if (typeof authorizeResponse === 'string') {
197
199
  htmlContent = authorizeResponse;
198
200
  }
199
201
  else if (authorizeResponse && typeof authorizeResponse === 'object') {
200
202
  const respObj = authorizeResponse;
201
203
  const respHeaders = respObj.headers;
202
- // Check for redirect in headers
203
- if (respHeaders && respHeaders.location) {
204
- currentUrl = respHeaders.location;
204
+ // Check for redirect in headers (case-insensitive)
205
+ if (respHeaders) {
206
+ const locationHeader = respHeaders.location || respHeaders.Location;
207
+ if (locationHeader) {
208
+ currentUrl = locationHeader;
209
+ }
205
210
  }
206
211
  if (respObj.body && typeof respObj.body === 'string') {
207
212
  htmlContent = respObj.body;
@@ -216,28 +221,116 @@ async function vwLogin(context, email, password) {
216
221
  else {
217
222
  htmlContent = String(authorizeResponse);
218
223
  }
219
- // Try to extract state token from Auth0 page (new flow)
220
- // Look for state in URL or in hidden form field
221
- let stateToken = '';
222
- // Check if we were redirected to /u/login
223
- const stateUrlMatch = currentUrl.match(/state=([^&]+)/);
224
- if (stateUrlMatch) {
225
- stateToken = decodeURIComponent(stateUrlMatch[1]);
224
+ // Follow redirects manually to capture the state parameter
225
+ let maxInitialRedirects = 5;
226
+ while (currentUrl && maxInitialRedirects > 0) {
227
+ // Extract state from redirect URL
228
+ const stateMatch = currentUrl.match(/state=([^&]+)/);
229
+ if (stateMatch) {
230
+ stateToken = decodeURIComponent(stateMatch[1]);
231
+ }
232
+ // If we reached the /u/login page, we have what we need
233
+ if (currentUrl.includes('/u/login') && stateToken) {
234
+ break;
235
+ }
236
+ // Follow the redirect
237
+ const followUrl = currentUrl.startsWith('http') ? currentUrl : `https://identity.vwgroup.io${currentUrl}`;
238
+ const followResponse = await context.helpers.httpRequest({
239
+ method: 'GET',
240
+ url: followUrl,
241
+ headers: {
242
+ 'User-Agent': 'Mozilla/5.0 (Linux; Android 12; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
243
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
244
+ },
245
+ encoding: 'text',
246
+ returnFullResponse: true,
247
+ ignoreHttpStatusErrors: true,
248
+ skipAutoFollowRedirects: true,
249
+ });
250
+ if (typeof followResponse === 'string') {
251
+ htmlContent = followResponse;
252
+ currentUrl = '';
253
+ }
254
+ else if (followResponse && typeof followResponse === 'object') {
255
+ const respObj = followResponse;
256
+ const respHeaders = respObj.headers;
257
+ currentUrl = '';
258
+ if (respHeaders) {
259
+ const locationHeader = respHeaders.location || respHeaders.Location;
260
+ if (locationHeader) {
261
+ currentUrl = locationHeader;
262
+ }
263
+ }
264
+ if (respObj.body && typeof respObj.body === 'string') {
265
+ htmlContent = respObj.body;
266
+ }
267
+ else if (respObj.data && typeof respObj.data === 'string') {
268
+ htmlContent = respObj.data;
269
+ }
270
+ }
271
+ maxInitialRedirects--;
226
272
  }
227
- // Also try to find state in the HTML
273
+ // Try to extract state token from the final HTML if not found in URL
274
+ // Auth0 embeds the state in various ways
275
+ // Method 1: Hidden form field
228
276
  if (!stateToken) {
229
277
  const stateHtmlMatch = htmlContent.match(/name="state"\s+value="([^"]+)"/);
230
278
  if (stateHtmlMatch) {
231
279
  stateToken = stateHtmlMatch[1];
232
280
  }
233
281
  }
234
- // Try to find state in form action URL
282
+ // Method 2: Form action URL
235
283
  if (!stateToken) {
236
284
  const formActionMatch = htmlContent.match(/action="[^"]*\?state=([^"&]+)/);
237
285
  if (formActionMatch) {
238
286
  stateToken = decodeURIComponent(formActionMatch[1]);
239
287
  }
240
288
  }
289
+ // Method 3: JavaScript variable or config object
290
+ if (!stateToken) {
291
+ const jsStateMatch = htmlContent.match(/"state"\s*:\s*"([^"]+)"/);
292
+ if (jsStateMatch) {
293
+ stateToken = jsStateMatch[1];
294
+ }
295
+ }
296
+ // Method 4: data-state attribute
297
+ if (!stateToken) {
298
+ const dataStateMatch = htmlContent.match(/data-state="([^"]+)"/);
299
+ if (dataStateMatch) {
300
+ stateToken = dataStateMatch[1];
301
+ }
302
+ }
303
+ // Method 5: Auth0 config in script tag
304
+ if (!stateToken) {
305
+ const auth0ConfigMatch = htmlContent.match(/config\s*=\s*\{[^}]*state[^}]*\}/);
306
+ if (auth0ConfigMatch) {
307
+ const configStateMatch = auth0ConfigMatch[0].match(/state['"]\s*:\s*['"]([^'"]+)['"]/);
308
+ if (configStateMatch) {
309
+ stateToken = configStateMatch[1];
310
+ }
311
+ }
312
+ }
313
+ // Method 6: URL in any href or src with state parameter
314
+ if (!stateToken) {
315
+ const hrefStateMatch = htmlContent.match(/(?:href|src|action)="[^"]*[?&]state=([^"&]+)/);
316
+ if (hrefStateMatch) {
317
+ stateToken = decodeURIComponent(hrefStateMatch[1]);
318
+ }
319
+ }
320
+ // Method 7: Any state= pattern in HTML (last resort)
321
+ if (!stateToken) {
322
+ const anyStateMatch = htmlContent.match(/state=([a-zA-Z0-9_.-]+)/);
323
+ if (anyStateMatch) {
324
+ stateToken = anyStateMatch[1];
325
+ }
326
+ }
327
+ // Method 8: Extract from window.__AUTH0_STATE or similar
328
+ if (!stateToken) {
329
+ const windowStateMatch = htmlContent.match(/__(?:AUTH0_)?STATE__?\s*=\s*['"]([^'"]+)['"]/i);
330
+ if (windowStateMatch) {
331
+ stateToken = windowStateMatch[1];
332
+ }
333
+ }
241
334
  // Try legacy CSRF-based flow first
242
335
  const csrfMatch = htmlContent.match(/name="_csrf"\s+value="([^"]+)"/);
243
336
  const relayStateMatch = htmlContent.match(/name="relayState"\s+value="([^"]+)"/);
@@ -339,7 +432,11 @@ async function vwLogin(context, email, password) {
339
432
  }
340
433
  }
341
434
  if (!stateToken) {
342
- throw new Error(`Could not extract state token from Auth0 login page. Response preview: ${htmlContent.substring(0, 500)}`);
435
+ // Show more of the HTML to help debug
436
+ const htmlPreview = htmlContent.substring(0, 2000);
437
+ const formMatch = htmlContent.match(/<form[^>]*>[\s\S]*?<\/form>/i);
438
+ const formPreview = formMatch ? formMatch[0].substring(0, 500) : 'No form found';
439
+ throw new Error(`Could not extract state token. Form: ${formPreview} | HTML preview: ${htmlPreview}`);
343
440
  }
344
441
  // Submit credentials to Auth0 /u/login endpoint
345
442
  const loginResponse = await context.helpers.httpRequest({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-jygse-vw-weconnect",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "n8n community node for VW We Connect - Control your Volkswagen T6.1 and other VW vehicles",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",