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
|
|
204
|
-
|
|
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
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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