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
- // Step 1: Get authorization page and extract form data
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: generateNonce(),
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 CSRF token and relay state from the response
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
- // Try different possible response structures
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
- const csrfMatch = htmlContent.match(/name="_csrf"\s+value="([^"]+)"/);
216
- const relayStateMatch = htmlContent.match(/name="relayState"\s+value="([^"]+)"/);
217
- const hmacMatch = htmlContent.match(/name="hmac"\s+value="([^"]+)"/);
218
- if (!csrfMatch) {
219
- // Show first 500 chars of response for debugging
220
- const preview = htmlContent.substring(0, 500);
221
- throw new Error(`Could not extract CSRF token. Response preview: ${preview}`);
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
- else if (identifierResponse && typeof identifierResponse === 'object') {
251
- const respObj = identifierResponse;
252
- identifierHtml = respObj.body || JSON.stringify(respObj);
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
- else {
255
- identifierHtml = String(identifierResponse);
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
- const csrf2Match = identifierHtml.match(/name="_csrf"\s+value="([^"]+)"/);
258
- const relayState2Match = identifierHtml.match(/name="relayState"\s+value="([^"]+)"/);
259
- const hmac2Match = identifierHtml.match(/name="hmac"\s+value="([^"]+)"/);
260
- const csrf2 = csrf2Match ? csrf2Match[1] : csrf;
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 (typeof authResponse === 'string') {
287
- authHtml = authResponse;
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 (authResponse && typeof authResponse === 'object') {
290
- const respObj = authResponse;
291
- const respHeaders = respObj.headers;
292
- if (respHeaders && respHeaders.location) {
293
- redirectUrl = respHeaders.location;
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
- authHtml = respObj.body || '';
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-jygse-vw-weconnect",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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",