n8n-nodes-jygse-vw-weconnect 0.1.3 → 0.1.4
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.
|
@@ -166,7 +166,8 @@ async function vwLogin(context, email, password) {
|
|
|
166
166
|
// Generate PKCE values
|
|
167
167
|
const codeVerifier = generateCodeVerifier();
|
|
168
168
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
169
|
-
|
|
169
|
+
const stateParam = generateNonce();
|
|
170
|
+
// Step 1: Get authorization page - this now returns Auth0 login page
|
|
170
171
|
const authorizeUrl = 'https://identity.vwgroup.io/oidc/v1/authorize';
|
|
171
172
|
const authorizeParams = new URLSearchParams({
|
|
172
173
|
client_id: VW_CLIENT_ID,
|
|
@@ -174,7 +175,7 @@ async function vwLogin(context, email, password) {
|
|
|
174
175
|
response_type: VW_RESPONSE_TYPE,
|
|
175
176
|
redirect_uri: VW_REDIRECT_URI,
|
|
176
177
|
nonce: generateNonce(),
|
|
177
|
-
state:
|
|
178
|
+
state: stateParam,
|
|
178
179
|
code_challenge: codeChallenge,
|
|
179
180
|
code_challenge_method: 'plain',
|
|
180
181
|
});
|
|
@@ -189,15 +190,19 @@ async function vwLogin(context, email, password) {
|
|
|
189
190
|
returnFullResponse: true,
|
|
190
191
|
ignoreHttpStatusErrors: true,
|
|
191
192
|
});
|
|
192
|
-
// Extract
|
|
193
|
-
// Handle both string response and object with body property
|
|
193
|
+
// Extract response body and check for redirect
|
|
194
194
|
let htmlContent;
|
|
195
|
+
let currentUrl = '';
|
|
195
196
|
if (typeof authorizeResponse === 'string') {
|
|
196
197
|
htmlContent = authorizeResponse;
|
|
197
198
|
}
|
|
198
199
|
else if (authorizeResponse && typeof authorizeResponse === 'object') {
|
|
199
200
|
const respObj = authorizeResponse;
|
|
200
|
-
|
|
201
|
+
const respHeaders = respObj.headers;
|
|
202
|
+
// Check for redirect in headers
|
|
203
|
+
if (respHeaders && respHeaders.location) {
|
|
204
|
+
currentUrl = respHeaders.location;
|
|
205
|
+
}
|
|
201
206
|
if (respObj.body && typeof respObj.body === 'string') {
|
|
202
207
|
htmlContent = respObj.body;
|
|
203
208
|
}
|
|
@@ -205,94 +210,172 @@ async function vwLogin(context, email, password) {
|
|
|
205
210
|
htmlContent = respObj.data;
|
|
206
211
|
}
|
|
207
212
|
else {
|
|
208
|
-
// Serialize the object to see its structure in the error
|
|
209
213
|
htmlContent = JSON.stringify(respObj);
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
else {
|
|
213
217
|
htmlContent = String(authorizeResponse);
|
|
214
218
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
const csrf = csrfMatch[1];
|
|
224
|
-
const relayState = relayStateMatch ? relayStateMatch[1] : '';
|
|
225
|
-
const hmac = hmacMatch ? hmacMatch[1] : '';
|
|
226
|
-
// Step 2: Submit email
|
|
227
|
-
const identifierResponse = await context.helpers.httpRequest({
|
|
228
|
-
method: 'POST',
|
|
229
|
-
url: 'https://identity.vwgroup.io/signin-service/v1/signin/login/identifier',
|
|
230
|
-
headers: {
|
|
231
|
-
'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',
|
|
232
|
-
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
233
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
234
|
-
},
|
|
235
|
-
body: new URLSearchParams({
|
|
236
|
-
_csrf: csrf,
|
|
237
|
-
relayState: relayState,
|
|
238
|
-
hmac: hmac,
|
|
239
|
-
email: email,
|
|
240
|
-
}).toString(),
|
|
241
|
-
encoding: 'text',
|
|
242
|
-
returnFullResponse: true,
|
|
243
|
-
ignoreHttpStatusErrors: true,
|
|
244
|
-
});
|
|
245
|
-
// Extract new CSRF for password submission
|
|
246
|
-
let identifierHtml;
|
|
247
|
-
if (typeof identifierResponse === 'string') {
|
|
248
|
-
identifierHtml = identifierResponse;
|
|
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]);
|
|
249
226
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
227
|
+
// Also try to find state in the HTML
|
|
228
|
+
if (!stateToken) {
|
|
229
|
+
const stateHtmlMatch = htmlContent.match(/name="state"\s+value="([^"]+)"/);
|
|
230
|
+
if (stateHtmlMatch) {
|
|
231
|
+
stateToken = stateHtmlMatch[1];
|
|
232
|
+
}
|
|
253
233
|
}
|
|
254
|
-
|
|
255
|
-
|
|
234
|
+
// Try to find state in form action URL
|
|
235
|
+
if (!stateToken) {
|
|
236
|
+
const formActionMatch = htmlContent.match(/action="[^"]*\?state=([^"&]+)/);
|
|
237
|
+
if (formActionMatch) {
|
|
238
|
+
stateToken = decodeURIComponent(formActionMatch[1]);
|
|
239
|
+
}
|
|
256
240
|
}
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
const relayState2 = relayState2Match ? relayState2Match[1] : relayState;
|
|
262
|
-
const hmac2 = hmac2Match ? hmac2Match[1] : hmac;
|
|
263
|
-
// Step 3: Submit password
|
|
264
|
-
const authResponse = await context.helpers.httpRequest({
|
|
265
|
-
method: 'POST',
|
|
266
|
-
url: 'https://identity.vwgroup.io/signin-service/v1/signin/login/authenticate',
|
|
267
|
-
headers: {
|
|
268
|
-
'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',
|
|
269
|
-
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
270
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
271
|
-
},
|
|
272
|
-
body: new URLSearchParams({
|
|
273
|
-
_csrf: csrf2,
|
|
274
|
-
relayState: relayState2,
|
|
275
|
-
hmac: hmac2,
|
|
276
|
-
email: email,
|
|
277
|
-
password: password,
|
|
278
|
-
}).toString(),
|
|
279
|
-
encoding: 'text',
|
|
280
|
-
returnFullResponse: true,
|
|
281
|
-
ignoreHttpStatusErrors: true,
|
|
282
|
-
});
|
|
283
|
-
// Get the redirect URL which contains the authorization code
|
|
241
|
+
// Try legacy CSRF-based flow first
|
|
242
|
+
const csrfMatch = htmlContent.match(/name="_csrf"\s+value="([^"]+)"/);
|
|
243
|
+
const relayStateMatch = htmlContent.match(/name="relayState"\s+value="([^"]+)"/);
|
|
244
|
+
const hmacMatch = htmlContent.match(/name="hmac"\s+value="([^"]+)"/);
|
|
284
245
|
let redirectUrl = '';
|
|
285
246
|
let authHtml = '';
|
|
286
|
-
if (
|
|
287
|
-
|
|
247
|
+
if (csrfMatch) {
|
|
248
|
+
// Legacy flow with CSRF token
|
|
249
|
+
const csrf = csrfMatch[1];
|
|
250
|
+
const relayState = relayStateMatch ? relayStateMatch[1] : '';
|
|
251
|
+
const hmac = hmacMatch ? hmacMatch[1] : '';
|
|
252
|
+
// Step 2: Submit email
|
|
253
|
+
const identifierResponse = await context.helpers.httpRequest({
|
|
254
|
+
method: 'POST',
|
|
255
|
+
url: 'https://identity.vwgroup.io/signin-service/v1/signin/login/identifier',
|
|
256
|
+
headers: {
|
|
257
|
+
'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',
|
|
258
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
259
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
260
|
+
},
|
|
261
|
+
body: new URLSearchParams({
|
|
262
|
+
_csrf: csrf,
|
|
263
|
+
relayState: relayState,
|
|
264
|
+
hmac: hmac,
|
|
265
|
+
email: email,
|
|
266
|
+
}).toString(),
|
|
267
|
+
encoding: 'text',
|
|
268
|
+
returnFullResponse: true,
|
|
269
|
+
ignoreHttpStatusErrors: true,
|
|
270
|
+
});
|
|
271
|
+
let identifierHtml;
|
|
272
|
+
if (typeof identifierResponse === 'string') {
|
|
273
|
+
identifierHtml = identifierResponse;
|
|
274
|
+
}
|
|
275
|
+
else if (identifierResponse && typeof identifierResponse === 'object') {
|
|
276
|
+
const respObj = identifierResponse;
|
|
277
|
+
identifierHtml = respObj.body || JSON.stringify(respObj);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
identifierHtml = String(identifierResponse);
|
|
281
|
+
}
|
|
282
|
+
const csrf2Match = identifierHtml.match(/name="_csrf"\s+value="([^"]+)"/);
|
|
283
|
+
const relayState2Match = identifierHtml.match(/name="relayState"\s+value="([^"]+)"/);
|
|
284
|
+
const hmac2Match = identifierHtml.match(/name="hmac"\s+value="([^"]+)"/);
|
|
285
|
+
const csrf2 = csrf2Match ? csrf2Match[1] : csrf;
|
|
286
|
+
const relayState2 = relayState2Match ? relayState2Match[1] : relayState;
|
|
287
|
+
const hmac2 = hmac2Match ? hmac2Match[1] : hmac;
|
|
288
|
+
// Step 3: Submit password
|
|
289
|
+
const authResponse = await context.helpers.httpRequest({
|
|
290
|
+
method: 'POST',
|
|
291
|
+
url: 'https://identity.vwgroup.io/signin-service/v1/signin/login/authenticate',
|
|
292
|
+
headers: {
|
|
293
|
+
'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',
|
|
294
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
295
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
296
|
+
},
|
|
297
|
+
body: new URLSearchParams({
|
|
298
|
+
_csrf: csrf2,
|
|
299
|
+
relayState: relayState2,
|
|
300
|
+
hmac: hmac2,
|
|
301
|
+
email: email,
|
|
302
|
+
password: password,
|
|
303
|
+
}).toString(),
|
|
304
|
+
encoding: 'text',
|
|
305
|
+
returnFullResponse: true,
|
|
306
|
+
ignoreHttpStatusErrors: true,
|
|
307
|
+
});
|
|
308
|
+
if (typeof authResponse === 'string') {
|
|
309
|
+
authHtml = authResponse;
|
|
310
|
+
}
|
|
311
|
+
else if (authResponse && typeof authResponse === 'object') {
|
|
312
|
+
const respObj = authResponse;
|
|
313
|
+
const respHeaders = respObj.headers;
|
|
314
|
+
if (respHeaders && respHeaders.location) {
|
|
315
|
+
redirectUrl = respHeaders.location;
|
|
316
|
+
}
|
|
317
|
+
authHtml = respObj.body || '';
|
|
318
|
+
}
|
|
288
319
|
}
|
|
289
|
-
else if (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if (
|
|
293
|
-
|
|
320
|
+
else if (stateToken || htmlContent.includes('auth0.com') || htmlContent.includes('/u/login')) {
|
|
321
|
+
// New Auth0-based flow
|
|
322
|
+
// Extract state from the login page URL if not already found
|
|
323
|
+
if (!stateToken) {
|
|
324
|
+
// Try to get state from the response URL or form
|
|
325
|
+
const loginFormMatch = htmlContent.match(/action="([^"]*\/u\/login[^"]*)"/);
|
|
326
|
+
if (loginFormMatch) {
|
|
327
|
+
const formUrl = loginFormMatch[1];
|
|
328
|
+
const formStateMatch = formUrl.match(/state=([^&"]+)/);
|
|
329
|
+
if (formStateMatch) {
|
|
330
|
+
stateToken = decodeURIComponent(formStateMatch[1]);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
294
333
|
}
|
|
295
|
-
|
|
334
|
+
if (!stateToken) {
|
|
335
|
+
// Last resort: extract from any state parameter in the HTML
|
|
336
|
+
const anyStateMatch = htmlContent.match(/state=([a-zA-Z0-9_-]+)/);
|
|
337
|
+
if (anyStateMatch) {
|
|
338
|
+
stateToken = anyStateMatch[1];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (!stateToken) {
|
|
342
|
+
throw new Error(`Could not extract state token from Auth0 login page. Response preview: ${htmlContent.substring(0, 500)}`);
|
|
343
|
+
}
|
|
344
|
+
// Submit credentials to Auth0 /u/login endpoint
|
|
345
|
+
const loginResponse = await context.helpers.httpRequest({
|
|
346
|
+
method: 'POST',
|
|
347
|
+
url: `https://identity.vwgroup.io/u/login?state=${encodeURIComponent(stateToken)}`,
|
|
348
|
+
headers: {
|
|
349
|
+
'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',
|
|
350
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
351
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
352
|
+
'Origin': 'https://identity.vwgroup.io',
|
|
353
|
+
'Referer': `https://identity.vwgroup.io/u/login?state=${encodeURIComponent(stateToken)}`,
|
|
354
|
+
},
|
|
355
|
+
body: new URLSearchParams({
|
|
356
|
+
state: stateToken,
|
|
357
|
+
username: email,
|
|
358
|
+
password: password,
|
|
359
|
+
action: 'default',
|
|
360
|
+
}).toString(),
|
|
361
|
+
encoding: 'text',
|
|
362
|
+
returnFullResponse: true,
|
|
363
|
+
ignoreHttpStatusErrors: true,
|
|
364
|
+
});
|
|
365
|
+
if (typeof loginResponse === 'string') {
|
|
366
|
+
authHtml = loginResponse;
|
|
367
|
+
}
|
|
368
|
+
else if (loginResponse && typeof loginResponse === 'object') {
|
|
369
|
+
const respObj = loginResponse;
|
|
370
|
+
const respHeaders = respObj.headers;
|
|
371
|
+
if (respHeaders && respHeaders.location) {
|
|
372
|
+
redirectUrl = respHeaders.location;
|
|
373
|
+
}
|
|
374
|
+
authHtml = respObj.body || '';
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
throw new Error(`Unknown login page format. Response preview: ${htmlContent.substring(0, 500)}`);
|
|
296
379
|
}
|
|
297
380
|
if (!redirectUrl && authHtml) {
|
|
298
381
|
// Check if we got a redirect in the response HTML
|
package/package.json
CHANGED