nodejs-insta-private-api-mqt 1.3.80 → 1.3.82

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.
@@ -168,10 +168,7 @@ class State {
168
168
 
169
169
  extractCookieValue(key) {
170
170
  const cookie = this.extractCookie(key);
171
- if (!cookie) {
172
- throw new Error(`Could not find cookie: ${key}`);
173
- }
174
- return cookie.value;
171
+ return cookie ? cookie.value : null;
175
172
  }
176
173
 
177
174
  get cookieCsrfToken() {
@@ -310,7 +307,8 @@ class State {
310
307
  connectionTypeHeader: this.connectionTypeHeader,
311
308
  capabilitiesHeader: this.capabilitiesHeader,
312
309
  requestId: this.requestId,
313
- traySessionId: this.traySessionId
310
+ traySessionId: this.traySessionId,
311
+ userId: this._userId || null
314
312
  };
315
313
  return obj;
316
314
  }
@@ -357,6 +355,11 @@ class State {
357
355
  }
358
356
  }
359
357
 
358
+ // If serialized state carried a raw userId, restore it into the internal field
359
+ if (obj.userId && !this._userId) {
360
+ this._userId = obj.userId;
361
+ }
362
+
360
363
  // refresh parsed authorization (if any)
361
364
  this.updateAuthorization();
362
365
  }
@@ -25,6 +25,284 @@ class AccountRepository extends Repository {
25
25
  }
26
26
  }
27
27
 
28
+ /**
29
+ * Helper: try to extract an IGT bearer token from various sources:
30
+ * - response.headers (IG-Set-Authorization or ig-set-authorization)
31
+ * - response.body top-level fields
32
+ * - body.layout (stringified layout)
33
+ * - any embedded login_response.headers JSON inside layout or body
34
+ *
35
+ * When found, sets this.client.state.authorization and calls updateAuthorization().
36
+ */
37
+ _extractAndSaveAuthorization(response) {
38
+ if (!response) return null;
39
+
40
+ // Normalize headers (case-insensitive)
41
+ const headers = {};
42
+ if (response.headers && typeof response.headers === 'object') {
43
+ for (const k of Object.keys(response.headers)) {
44
+ headers[k.toLowerCase()] = response.headers[k];
45
+ }
46
+ }
47
+
48
+ // Simpler: look for any header that contains ig-set-authorization
49
+ for (const key of Object.keys(headers)) {
50
+ const val = headers[key];
51
+ if (!val) continue;
52
+ if (key.includes('ig-set-authorization')) {
53
+ const token = this._normalizeTokenString(val);
54
+ if (token) {
55
+ this.client.state.authorization = token;
56
+ try { this.client.state.updateAuthorization(); } catch (e) {}
57
+ return token;
58
+ }
59
+ }
60
+ // Some servers include the header value inside a JSON-like string in other headers
61
+ if (typeof val === 'string' && val.includes('Bearer IGT:2:')) {
62
+ const token = this._findBearerInString(val);
63
+ if (token) {
64
+ this.client.state.authorization = token;
65
+ try { this.client.state.updateAuthorization(); } catch (e) {}
66
+ return token;
67
+ }
68
+ }
69
+ }
70
+
71
+ // Extract IG-U headers (user id, region, claim) when present
72
+ const dsUserIdHeader =
73
+ headers['ig-set-ig-u-ds-user-id'] ||
74
+ headers['ig-u-ds-user-id'] ||
75
+ headers['x-ig-set-ig-u-ds-user-id'];
76
+ if (dsUserIdHeader) {
77
+ const m = String(dsUserIdHeader).match(/(\d{3,})/);
78
+ if (m) {
79
+ const uid = m[1];
80
+ this.client.state.cookieUserId = uid;
81
+ this.client.state._userId = uid;
82
+ }
83
+ }
84
+
85
+ const rurHeader =
86
+ headers['ig-set-ig-u-rur'] ||
87
+ headers['ig-u-rur'] ||
88
+ headers['x-ig-set-ig-u-rur'];
89
+ if (rurHeader) {
90
+ const rur = String(rurHeader).trim().replace(/^"|"$/g, '');
91
+ if (rur) {
92
+ this.client.state.igURur = rur;
93
+ }
94
+ }
95
+
96
+ const wwwClaimHeader =
97
+ headers['x-ig-set-www-claim'] ||
98
+ headers['ig-set-www-claim'] ||
99
+ headers['ig-u-www-claim'];
100
+ if (wwwClaimHeader) {
101
+ const claim = String(wwwClaimHeader).trim().replace(/^"|"$/g, '');
102
+ if (claim) {
103
+ this.client.state.igWWWClaim = claim;
104
+ }
105
+ }
106
+
107
+ const midHeader =
108
+ headers['ig-set-x-mid'] ||
109
+ headers['x-mid'] ||
110
+ headers['ig-set-mid'];
111
+ if (midHeader) {
112
+ const mid = String(midHeader).trim().replace(/^"|"$/g, '');
113
+ if (mid) {
114
+ this.client.state.mid = mid;
115
+ }
116
+ }
117
+
118
+ // 2) Check response.body for common fields
119
+ const body = response.body;
120
+ if (body) {
121
+ // If body has headers string (some responses embed headers JSON as a string)
122
+ if (body.headers && typeof body.headers === 'string') {
123
+ const token = this._findBearerInString(body.headers);
124
+ if (token) {
125
+ this.client.state.authorization = token;
126
+ try { this.client.state.updateAuthorization(); } catch (e) {}
127
+ return token;
128
+ }
129
+ }
130
+
131
+ // If body contains a layout object or string, stringify and search it
132
+ try {
133
+ let layoutStr = null;
134
+ if (body.layout) {
135
+ layoutStr = typeof body.layout === 'string' ? body.layout : JSON.stringify(body.layout);
136
+ } else if (body?.layout?.bloks_payload) {
137
+ layoutStr = JSON.stringify(body.layout.bloks_payload);
138
+ } else if (body?.bloks_payload) {
139
+ layoutStr = JSON.stringify(body.bloks_payload);
140
+ }
141
+
142
+ if (!layoutStr && typeof body === 'string') {
143
+ layoutStr = body;
144
+ }
145
+
146
+ if (layoutStr) {
147
+ const token = this._findBearerInString(layoutStr);
148
+ if (token) {
149
+ this.client.state.authorization = token;
150
+ try { this.client.state.updateAuthorization(); } catch (e) {}
151
+ return token;
152
+ }
153
+
154
+ // Some responses embed a login_response JSON string inside layout; try to extract it
155
+ const loginResponseMatch = layoutStr.match(/"login_response"\s*:\s*"(\\?{.*?\\?})"/s);
156
+ if (loginResponseMatch) {
157
+ // Unescape JSON string
158
+ let jsonStr = loginResponseMatch[1];
159
+ try {
160
+ jsonStr = jsonStr.replace(/\\"/g, '"').replace(/\\n/g, '');
161
+ const parsed = JSON.parse(jsonStr);
162
+ if (parsed && parsed.headers) {
163
+ const token = this._findBearerInString(parsed.headers);
164
+ if (token) {
165
+ this.client.state.authorization = token;
166
+ try { this.client.state.updateAuthorization(); } catch (e) {}
167
+ return token;
168
+ }
169
+ }
170
+ } catch (e) {
171
+ // ignore parse errors
172
+ }
173
+ }
174
+
175
+ // Also try to find IG-Set-Password-Encryption headers (not token but useful)
176
+ const encKeyIdMatch = layoutStr.match(/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i) ||
177
+ layoutStr.match(/password.encryption.key.id[\\"\s:]+(\d+)/i);
178
+ if (encKeyIdMatch) {
179
+ this.client.state.passwordEncryptionKeyId = parseInt(encKeyIdMatch[1]);
180
+ }
181
+ const encPubKeyMatch = layoutStr.match(/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i) ||
182
+ layoutStr.match(/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i);
183
+ if (encPubKeyMatch) {
184
+ this.client.state.passwordEncryptionPubKey = encPubKeyMatch[1];
185
+ }
186
+
187
+ // Extract user/account identifiers from layout string
188
+ const dsIdMatch =
189
+ layoutStr.match(/ig-set-ig-u-ds-user-id[\\"\s:]+(\d+)/i) ||
190
+ layoutStr.match(/ig-u-ds-user-id[\\"\s:]+(\d+)/i) ||
191
+ layoutStr.match(/"strong_id__"\s*:\s*"(\d+)"/i) ||
192
+ layoutStr.match(/"pk_id"\s*:\s*"(\d+)"/i) ||
193
+ layoutStr.match(/"pk"\s*:\s*(\d+)/i);
194
+ if (dsIdMatch) {
195
+ const uid = String(dsIdMatch[1]);
196
+ this.client.state.cookieUserId = uid;
197
+ this.client.state._userId = uid;
198
+ }
199
+
200
+ const rurMatch =
201
+ layoutStr.match(/ig-set-ig-u-rur[\\"\s:]+"([^"\\]+)/i) ||
202
+ layoutStr.match(/ig-u-rur[\\"\s:]+"([^"\\]+)/i);
203
+ if (rurMatch) {
204
+ this.client.state.igURur = rurMatch[1];
205
+ }
206
+
207
+ const wwwClaimMatch = layoutStr.match(/x-ig-set-www-claim[\\"\s:]+"([^"\\]+)/i);
208
+ if (wwwClaimMatch) {
209
+ this.client.state.igWWWClaim = wwwClaimMatch[1];
210
+ }
211
+
212
+ const midMatch = layoutStr.match(/"mid"\s*:\s*"([^"\\]+)"/i);
213
+ if (midMatch) {
214
+ this.client.state.mid = midMatch[1];
215
+ }
216
+ }
217
+ } catch (e) {
218
+ // ignore
219
+ }
220
+
221
+ // 3) If body.logged_in_user exists and we have authorization already, return it
222
+ if (body.logged_in_user && this.client.state.authorization) {
223
+ return this.client.state.authorization;
224
+ }
225
+ }
226
+
227
+ return null;
228
+ }
229
+
230
+ // Normalize token string: accept either raw token or "Bearer IGT:2:..." and return trimmed token string
231
+ _normalizeTokenString(val) {
232
+ if (!val || typeof val !== 'string') return null;
233
+ // If header already contains "Bearer IGT:2:..."
234
+ const bearer = this._findBearerInString(val);
235
+ if (bearer) return bearer;
236
+ // If header contains JSON with IG-Set-Authorization field
237
+ try {
238
+ const maybeJson = JSON.parse(val);
239
+ if (maybeJson['IG-Set-Authorization']) {
240
+ return this._findBearerInString(maybeJson['IG-Set-Authorization']);
241
+ }
242
+ } catch (e) {
243
+ // not JSON
244
+ }
245
+ // fallback: return trimmed value
246
+ return val.trim();
247
+ }
248
+
249
+ // Find bearer token in arbitrary string using multiple patterns
250
+ _findBearerInString(str) {
251
+ if (!str || typeof str !== 'string') return null;
252
+ // Patterns to match the Bearer token in many possible encodings/escapes
253
+ const bearerPatterns = [
254
+ /Bearer IGT:2:[A-Za-z0-9+\/=]+/,
255
+ /Bearer\s+IGT:2:[A-Za-z0-9+\/=]+/,
256
+ /\?"Bearer IGT:2:([A-Za-z0-9+\/=]+)\?"/,
257
+ /ig-set-authorization[\\"\s:]*Bearer IGT:2:([A-Za-z0-9+\/=]+)/i,
258
+ /"IG-Set-Authorization"\s*:\s*"(Bearer IGT:2:[A-Za-z0-9+\/=]+)"/i,
259
+ /IG-Set-Authorization[\s:]+(Bearer IGT:2:[A-Za-z0-9+\/=]+)/i,
260
+ /Bearer IGT:2:([A-Za-z0-9+\/=]+)/,
261
+ ];
262
+
263
+ for (const pattern of bearerPatterns) {
264
+ const m = str.match(pattern);
265
+ if (m) {
266
+ // If capturing group present, prefer it
267
+ if (m[1]) {
268
+ const token = m[1].includes('Bearer IGT:2:') ? m[1] : `Bearer IGT:2:${m[1]}`;
269
+ return token.replace(/\?"/g, '').trim();
270
+ }
271
+ // Otherwise m[0] contains the full token
272
+ return m[0].replace(/\?"/g, '').trim();
273
+ }
274
+ }
275
+
276
+ return null;
277
+ }
278
+
279
+ // Ensure we have a valid csrftoken cookie (and mid) before sensitive endpoints.
280
+ // Instagram typically sets csrftoken via /api/v1/si/fetch_headers/.
281
+ async ensureCsrfToken() {
282
+ const cookieToken = this.client.state.cookieCsrfToken;
283
+ if (cookieToken && cookieToken !== 'missing' && cookieToken !== 'pending') {
284
+ try { this.client.state.csrfToken = cookieToken; } catch {}
285
+ return cookieToken;
286
+ }
287
+
288
+ // Warmup endpoint that returns/set cookies (csrftoken, mid, etc.)
289
+ try {
290
+ await this.client.request.send({
291
+ method: 'GET',
292
+ url: `/api/v1/si/fetch_headers/?challenge_type=signup&guid=${this.client.state.uuid}`,
293
+ });
294
+ } catch (e) {
295
+ // ignore warmup failures
296
+ }
297
+
298
+ const token = this.client.state.cookieCsrfToken;
299
+ if (token && token !== 'missing' && token !== 'pending') {
300
+ try { this.client.state.csrfToken = token; } catch {}
301
+ return token;
302
+ }
303
+ return null;
304
+ }
305
+
28
306
  async login(credentialsOrUsername, passwordArg) {
29
307
  let username, password;
30
308
  if (typeof credentialsOrUsername === 'object' && credentialsOrUsername !== null) {
@@ -44,16 +322,53 @@ class AccountRepository extends Repository {
44
322
 
45
323
  const { encrypted, time } = this.encryptPassword(password);
46
324
 
325
+ // optional CSRF warmup (like app does before login)
326
+ await this.ensureCsrfToken();
327
+
47
328
  return this.requestWithRetry(async () => {
48
- const aacInitTimestamp = Math.floor(Date.now() / 1000) - Math.floor(Math.random() * 50);
329
+ // ====== client_input_params (oglindă după traficul real) ======
330
+ const nowSec = Math.floor(Date.now() / 1000);
331
+ const aacInitTimestamp = nowSec - Math.floor(Math.random() * 50);
49
332
  const aacjid = crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4();
50
- const aaccs = crypto.randomBytes(32).toString('base64url');
333
+ const aaccs = crypto.randomBytes(32).toString('base64').replace(/=/g, '');
334
+
335
+ const androidDeviceId =
336
+ this.client.state.androidDeviceId ||
337
+ this.client.state.deviceId ||
338
+ `android-${crypto.randomBytes(8).toString('hex')}`;
339
+
340
+ const machineId =
341
+ this.client.state.mid ||
342
+ this.client.state.machineId ||
343
+ `aZ${crypto.randomBytes(8).toString('hex')}`;
344
+
345
+ const familyDeviceId =
346
+ this.client.state.phoneId ||
347
+ this.client.state.familyDeviceId ||
348
+ crypto.randomUUID
349
+ ? crypto.randomUUID()
350
+ : require('uuid').v4();
351
+
352
+ const qeDeviceId =
353
+ this.client.state.deviceId ||
354
+ this.client.state.qeDeviceId ||
355
+ crypto.randomUUID
356
+ ? crypto.randomUUID()
357
+ : require('uuid').v4();
358
+
359
+ const accountsList = this.client.state.accountsList || [];
360
+
361
+ const flashCallPermissionStatus = {
362
+ READ_PHONE_STATE: 'GRANTED',
363
+ READ_CALL_LOG: 'GRANTED',
364
+ ANSWER_PHONE_CALLS: 'GRANTED',
365
+ };
51
366
 
52
367
  const clientInputParams = {
53
368
  aac: JSON.stringify({
54
369
  aac_init_timestamp: aacInitTimestamp,
55
- aacjid: aacjid,
56
- aaccs: aaccs,
370
+ aacjid,
371
+ aaccs,
57
372
  }),
58
373
  sim_phones: [],
59
374
  aymh_accounts: [],
@@ -63,25 +378,77 @@ class AccountRepository extends Repository {
63
378
  auth_secure_device_id: '',
64
379
  has_whatsapp_installed: 1,
65
380
  password: `#PWD_INSTAGRAM:4:${time}:${encrypted}`,
66
- sso_token_map_json_string: '{}',
381
+ sso_token_map_json_string: JSON.stringify({
382
+ [this.client.state.cookieUserId || '0']: [],
383
+ }),
67
384
  block_store_machine_id: '',
68
- ig_vetted_device_nonces: '{}',
385
+ ig_vetted_device_nonces: JSON.stringify({}),
386
+ cloud_trust_token: null,
387
+ event_flow: 'login_manual',
388
+ password_contains_non_ascii: 'false',
389
+ client_known_key_hash: '',
390
+ encrypted_msisdn: '',
391
+ has_granted_read_phone_permissions: 0,
392
+ app_manager_id: '',
393
+ should_show_nested_nta_from_aymh: 0,
394
+ device_id: androidDeviceId,
395
+ zero_balance_state: '',
396
+ login_attempt_count: 1,
397
+ machine_id: machineId,
398
+ flash_call_permission_status: flashCallPermissionStatus,
399
+ accounts_list: accountsList,
400
+ gms_incoming_call_retriever_eligibility: 'client_not_supported',
401
+ family_device_id: familyDeviceId,
402
+ fb_ig_device_id: [],
403
+ device_emails: [],
404
+ try_num: 1,
405
+ lois_settings: { lois_token: '' },
406
+ event_step: 'home_page',
407
+ headers_infra_flow_id: '',
408
+ openid_tokens: {},
69
409
  contact_point: username,
70
- machine_id: this.client.state.mid || '',
71
- login_attempt_count: '0',
72
- reg_flow_taken: 'phone',
73
- device_id: this.client.state.uuid,
74
- phone_id: this.client.state.phoneId,
75
- family_device_id: this.client.state.phoneId,
76
- encryption_enabled: '1',
77
- has_dbl_tap_login: 0,
78
- jazoest: AccountRepository.createJazoest(this.client.state.phoneId),
79
- openid_tokens: '{}',
80
410
  };
81
411
 
412
+ // ====== server_params (oglindă după traficul real) ======
413
+ const waterfallId = crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4();
414
+ const latencyMarkerId = 36707139;
415
+ const latencyInstanceId = Number(`${Date.now()}${Math.floor(Math.random() * 1000)}`);
416
+
82
417
  const serverParams = {
418
+ should_trigger_override_login_2fa_action: 0,
419
+ is_vanilla_password_page_empty_password: 0,
420
+ is_from_logged_out: 0,
421
+ should_trigger_override_login_success_action: 0,
422
+ login_credential_type: 'none',
423
+ server_login_source: 'login',
424
+ waterfall_id: waterfallId,
425
+ two_step_login_type: 'one_step_login',
426
+ login_source: 'Login',
427
+ is_platform_login: 0,
428
+ INTERNAL__latency_qpl_marker_id: latencyMarkerId,
429
+ is_from_aymh: 0,
430
+ offline_experiment_group: 'caa_iteration_v3_perf_ig_4',
431
+ is_from_landing_page: 0,
432
+ left_nav_button_action: 'NONE',
433
+ password_text_input_id: 'z0jejq:194',
434
+ is_from_empty_password: 0,
435
+ is_from_msplit_fallback: 0,
436
+ ar_event_source: 'login_home_page',
437
+ qe_device_id: qeDeviceId,
438
+ username_text_input_id: 'z0jejq:193',
439
+ layered_homepage_experiment_group: null,
440
+ device_id: androidDeviceId,
441
+ login_surface: 'switcher',
442
+ INTERNAL__latency_qpl_instance_id: latencyInstanceId,
443
+ reg_flow_source: 'login_home_native_integration_point',
444
+ is_caa_perf_enabled: 1,
83
445
  credential_type: 'password',
84
- device_id: this.client.state.uuid,
446
+ is_from_password_entry_page: 0,
447
+ caller: 'gslr',
448
+ family_device_id: familyDeviceId,
449
+ is_from_assistive_id: 0,
450
+ access_flow_version: 'pre_mt_behavior',
451
+ is_from_logged_in_switcher: 1,
85
452
  };
86
453
 
87
454
  const paramsJson = JSON.stringify({
@@ -91,73 +458,158 @@ class AccountRepository extends Repository {
91
458
 
92
459
  const attestParams = AccountRepository.generateAttestParams(this.client.state);
93
460
 
461
+ const bkClientContext = JSON.stringify({
462
+ bloks_version: this.client.state.bloksVersionId,
463
+ styles_id: 'instagram',
464
+ });
465
+
466
+ // ====== HEADERS – modelate după request_i.instagram.com_mlwzj8x1.txt ======
467
+ const lang = (this.client.state.language || 'ro_RO');
468
+ const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
469
+
470
+ const userAgent =
471
+ this.client.state.userAgent ||
472
+ 'Instagram 371.0.0.0.23 Android (30/11; 320dpi; 720x1449; Xiaomi/Redmi; M2006C3MNG; angelican; mt6765; ro_RO; 703217507)';
473
+
94
474
  const bloksHeaders = {
95
- 'X-Bloks-Prism-Ax-Base-Colors-Enabled': 'false',
96
- 'X-Bloks-Prism-Button-Version': 'CONTROL',
97
- 'X-Bloks-Prism-Colors-Enabled': 'true',
98
- 'X-Bloks-Prism-Font-Enabled': 'false',
99
- 'X-Bloks-Prism-Indigo-Link-Version': '0',
100
- 'X-FB-Friendly-Name': 'IgApi: bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
101
- 'X-FB-Request-Analytics-Tags': JSON.stringify({
475
+ 'accept-language': acceptLanguage,
476
+ 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
477
+ 'ig-intended-user-id': '0',
478
+ 'priority': 'u=3',
479
+
480
+ 'x-bloks-is-layout-rtl': 'false',
481
+ 'x-bloks-prism-ax-base-colors-enabled': 'false',
482
+ 'x-bloks-prism-button-version': 'CONTROL',
483
+ 'x-bloks-prism-colors-enabled': 'true',
484
+ 'x-bloks-prism-font-enabled': 'false',
485
+ 'x-bloks-prism-indigo-link-version': '0',
486
+ 'x-bloks-version-id': this.client.state.bloksVersionId,
487
+
488
+ 'x-fb-client-ip': 'True',
489
+ 'x-fb-connection-type': 'WIFI',
490
+ 'x-fb-friendly-name': 'IgApi: bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
491
+ 'x-fb-network-properties': 'VPN;Validated;LocalAddrs=/10.0.0.2,;',
492
+ 'x-fb-request-analytics-tags': JSON.stringify({
102
493
  network_tags: {
103
- product: this.client.state.fbAnalyticsApplicationId,
494
+ product: String(this.client.state.fbAnalyticsApplicationId || '567067343352427'),
104
495
  purpose: 'fetch',
105
496
  surface: 'undefined',
106
497
  request_category: 'api',
107
498
  retry_attempt: '0',
108
499
  },
109
500
  }),
110
- 'X-IG-Client-Endpoint': 'com.bloks.www.caa.login.aymh_single_profile_screen_entry',
111
- 'X-Tigon-Is-Retry': 'False',
112
- 'X-FB-RMD': 'state=URL_ELIGIBLE',
113
- 'X-IG-Attest-Params': JSON.stringify(attestParams),
114
- 'X-FB-Connection-Type': 'MOBILE.LTE',
115
- 'X-FB-Network-Properties': 'VPN;Metered;Validated;LocalAddrs=/10.0.0.2,;',
116
- 'X-FB-Conn-UUID-Client': crypto.randomBytes(16).toString('hex'),
117
- 'X-IG-Bandwidth-Speed-KBPS': (Math.random() * 1500 + 1500).toFixed(3),
118
- 'X-IG-Bandwidth-TotalBytes-B': '0',
119
- 'X-IG-Bandwidth-TotalTime-MS': '0',
120
- 'X-IG-Connection-Type': 'MOBILE(LTE)',
121
- 'X-IG-Capabilities': '3brTv10=',
122
- 'Accept-Encoding': 'gzip, deflate',
123
- 'X-FB-HTTP-Engine': 'MNS/TCP',
501
+ 'x-fb-server-cluster': 'True',
502
+
503
+ 'x-ig-android-id': androidDeviceId,
504
+ 'x-ig-app-id': String(this.client.state.fbAnalyticsApplicationId || '567067343352427'),
505
+ 'x-ig-app-locale': lang,
506
+ 'x-ig-attest-params': JSON.stringify(attestParams),
507
+ 'x-ig-bandwidth-speed-kbps': (Math.random() * 1500 + 800).toFixed(3),
508
+ 'x-ig-bandwidth-totalbytes-b': '0',
509
+ 'x-ig-bandwidth-totaltime-ms': '0',
510
+ 'x-ig-client-endpoint': 'com.bloks.www.caa.login.aymh_single_profile_screen_entry',
511
+ 'x-ig-capabilities': '3brTv10=',
512
+ 'x-ig-connection-type': 'WIFI',
513
+ 'x-ig-device-id': qeDeviceId,
514
+ 'x-ig-device-locale': lang,
515
+ 'x-ig-family-device-id': familyDeviceId,
516
+ 'x-ig-mapped-locale': lang,
517
+ 'x-ig-nav-chain':
518
+ 'LockoutFragment:dogfooding_lockout:1:cold_start:...,' +
519
+ 'com.bloks.www.caa.login.aymh_single_profile_screen_entry:com.bloks.www.caa.login.aymh_single_profile_screen_entry:14:button:0:::0',
520
+ 'x-ig-timezone-offset': String(
521
+ typeof this.client.state.timezoneOffset === 'number'
522
+ ? this.client.state.timezoneOffset
523
+ : 7200
524
+ ),
525
+ 'x-ig-www-claim': this.client.state.igWWWClaim || '0',
526
+
527
+ 'x-mid': machineId,
528
+ 'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(Math.random() * 1000)
529
+ .toString()
530
+ .padStart(3, '0')}`,
531
+ 'x-pigeon-session-id':
532
+ this.client.state.pigeonSessionId ||
533
+ `UFS-${crypto.randomBytes(16).toString('hex')}-1`,
534
+ 'x-tigon-is-retry': 'False',
535
+
536
+ // ...
537
+ 'accept-encoding': 'gzip, deflate, br',
538
+ // ...
539
+ 'user-agent': userAgent,
540
+ 'x-fb-conn-uuid-client': crypto.randomBytes(16).toString('hex'),
541
+ 'x-fb-http-engine': 'MNS/TCP',
542
+ 'x-fb-rmd': 'state=URL_ELIGIBLE',
124
543
  };
125
544
 
126
545
  const response = await this.client.request.send({
127
546
  method: 'POST',
128
547
  url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
129
- form: { params: paramsJson },
548
+ form: {
549
+ params: paramsJson,
550
+ bk_client_context: bkClientContext,
551
+ bloks_versioning_id: this.client.state.bloksVersionId,
552
+ },
130
553
  headers: bloksHeaders,
131
554
  });
132
555
 
556
+ // === DEBUG LOGIN: salvăm răspunsul pentru analiză ===
557
+ try {
558
+ const fs = require('fs');
559
+ const path = require('path');
560
+
561
+ const debugDir = path.join(process.cwd(), 'authinfo_instagram');
562
+ const debugFile = path.join(debugDir, 'login-debug.json');
563
+
564
+ try {
565
+ fs.mkdirSync(debugDir, { recursive: true });
566
+ } catch (e) {}
567
+
568
+ const debugPayload = {
569
+ at: new Date().toISOString(),
570
+ statusCode: response.statusCode || response.status || null,
571
+ headers: response.headers || null,
572
+ body: response.body || null,
573
+ };
574
+
575
+ fs.writeFileSync(debugFile, JSON.stringify(debugPayload, null, 2), 'utf8');
576
+ } catch (e) {
577
+ // nu stricăm login-ul dacă nu merge debug-ul
578
+ }
579
+ // === SFÂRȘIT DEBUG LOGIN ===
580
+
133
581
  const body = response.body;
134
582
 
135
- if (body.two_factor_required) {
583
+ // Immediately attempt to extract and save authorization token from the response
584
+ this._extractAndSaveAuthorization(response);
585
+
586
+ if (body && body.two_factor_required) {
136
587
  const err = new Error('Two factor authentication required');
137
588
  err.name = 'IgLoginTwoFactorRequiredError';
138
589
  err.twoFactorInfo = body.two_factor_info;
139
590
  throw err;
140
591
  }
141
- if (body.error_type === 'bad_password') {
592
+ if (body && body.error_type === 'bad_password') {
142
593
  const err = new Error('Bad password');
143
594
  err.name = 'IgLoginBadPasswordError';
144
595
  throw err;
145
596
  }
146
- if (body.error_type === 'invalid_user') {
597
+ if (body && body.error_type === 'invalid_user') {
147
598
  const err = new Error('Invalid user');
148
599
  err.name = 'IgLoginInvalidUserError';
149
600
  throw err;
150
601
  }
151
- if (body.message === 'challenge_required') {
602
+ if (body && body.message === 'challenge_required') {
152
603
  const err = new Error('Challenge required');
153
604
  err.name = 'IgCheckpointError';
154
605
  err.challengeInfo = body.challenge;
155
606
  throw err;
156
607
  }
157
608
 
158
- if (body.layout) {
609
+ if (body && body.layout) {
159
610
  const layoutStr = typeof body.layout === 'string' ? body.layout : JSON.stringify(body.layout);
160
611
 
612
+ // If layout contains two-factor markers, parse and throw
161
613
  if (layoutStr.includes('two_factor_required') || layoutStr.includes('"two_factor_info"')) {
162
614
  let twoFactorInfo = null;
163
615
  try {
@@ -189,24 +641,14 @@ class AccountRepository extends Repository {
189
641
  throw err;
190
642
  }
191
643
 
192
- const bearerPatterns = [
193
- /Bearer IGT:2:[A-Za-z0-9+\/=]+/,
194
- /Bearer\s+IGT:2:[A-Za-z0-9+\/=]+/,
195
- /\\?"Bearer IGT:2:([A-Za-z0-9+\/=]+)\\?"/,
196
- /ig-set-authorization[\\"\s:]*Bearer IGT:2:([A-Za-z0-9+\/=]+)/i,
197
- ];
198
- for (const pattern of bearerPatterns) {
199
- const bearerMatch = layoutStr.match(pattern);
200
- if (bearerMatch) {
201
- const token = bearerMatch[0].includes('Bearer IGT:2:')
202
- ? bearerMatch[0].replace(/^.*?(Bearer IGT:2:[A-Za-z0-9+\/=]+).*$/, '$1')
203
- : `Bearer IGT:2:${bearerMatch[1]}`;
204
- this.client.state.authorization = token.replace(/\\?"/g, '').trim();
205
- this.client.state.updateAuthorization();
206
- break;
207
- }
644
+ // Additional attempt to extract token from layout string (redundant but robust)
645
+ const tokenFromLayout = this._findBearerInString(layoutStr);
646
+ if (tokenFromLayout) {
647
+ this.client.state.authorization = tokenFromLayout;
648
+ try { this.client.state.updateAuthorization(); } catch (e) {}
208
649
  }
209
650
 
651
+ // Extract pk (user id) from layout if present
210
652
  const pkPatterns = [
211
653
  /\\"pk\\":\s*(\d+)/,
212
654
  /"pk":\s*(\d+)/,
@@ -226,6 +668,7 @@ class AccountRepository extends Repository {
226
668
  }
227
669
  }
228
670
 
671
+ // Extract IG-Set-WWW-Claim if present
229
672
  const wwwClaimPatterns = [
230
673
  /IG-Set-WWW-Claim[\\"\s:]+([a-f0-9]+)/i,
231
674
  /ig-set-www-claim[\\"\s:]+([a-f0-9]+)/i,
@@ -240,6 +683,7 @@ class AccountRepository extends Repository {
240
683
  }
241
684
  }
242
685
 
686
+ // Extract encryption key id and pub key (already attempted earlier in _extractAndSaveAuthorization)
243
687
  const encKeyIdMatch = layoutStr.match(/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i) ||
244
688
  layoutStr.match(/password.encryption.key.id[\\"\s:]+(\d+)/i);
245
689
  if (encKeyIdMatch) {
@@ -258,6 +702,7 @@ class AccountRepository extends Repository {
258
702
  this.client.state.mid = midMatch[1];
259
703
  }
260
704
 
705
+ // If layout contains a direct logged_in_user JSON, extract and return it
261
706
  const loginResponsePatterns = [
262
707
  /\\"logged_in_user\\":\{[^}]*\\"pk\\":(\d+)[^}]*\\"username\\":\\"([^"\\]+)\\"/,
263
708
  /"logged_in_user":\{[^}]*"pk":(\d+)[^}]*"username":"([^"]+)"/,
@@ -276,6 +721,7 @@ class AccountRepository extends Repository {
276
721
  }
277
722
  }
278
723
 
724
+ // If we have extracted a pk and we also have authorization, try to fetch current user
279
725
  if (extractedPk && this.client.state.authorization) {
280
726
  try {
281
727
  const userInfo = await this.currentUser();
@@ -291,15 +737,19 @@ class AccountRepository extends Repository {
291
737
  }
292
738
  }
293
739
 
294
- if (body.logged_in_user) {
740
+ // If server returned logged_in_user directly in body
741
+ if (body && body.logged_in_user) {
295
742
  const lu = body.logged_in_user;
296
743
  if (lu.pk) {
297
744
  this.client.state.cookieUserId = String(lu.pk);
298
745
  this.client.state._userId = String(lu.pk);
299
746
  }
747
+ // Ensure authorization is saved if present in response headers
748
+ this._extractAndSaveAuthorization(response);
300
749
  return lu;
301
750
  }
302
751
 
752
+ // If we already have authorization, try to fetch current user to populate state
303
753
  if (this.client.state.authorization) {
304
754
  try {
305
755
  const userInfo = await this.currentUser();
@@ -310,6 +760,7 @@ class AccountRepository extends Repository {
310
760
  }
311
761
  return user;
312
762
  } catch (e) {
763
+ // fallback: return raw body
313
764
  return body;
314
765
  }
315
766
  }
@@ -334,6 +785,8 @@ class AccountRepository extends Repository {
334
785
  phone_id: this.client.state.phoneId,
335
786
  }),
336
787
  });
788
+ // Save authorization if present in two-factor response
789
+ this._extractAndSaveAuthorization(response);
337
790
  return response.body;
338
791
  });
339
792
  }
@@ -776,7 +1229,7 @@ class AccountRepository extends Repository {
776
1229
  }
777
1230
 
778
1231
  static createJazoest(input) {
779
- const buf = Buffer.from(input, 'ascii');
1232
+ const buf = Buffer.from(input || '', 'ascii');
780
1233
  let sum = 0;
781
1234
  for (let i = 0; i < buf.byteLength; i++) {
782
1235
  sum += buf.readUInt8(i);
@@ -1227,6 +1227,30 @@ async function extractStateData(igState) {
1227
1227
  // try to obtain cookie fields
1228
1228
  const cookieFields = await extractCookieFields(igState);
1229
1229
 
1230
+ // try to decode ds_user_id & sessionid from Bearer IGT:2 token (authorization), if present
1231
+ let bearerDsUserId = null;
1232
+ let bearerSessionId = null;
1233
+ try {
1234
+ if (creds.authorization && typeof creds.authorization === 'string') {
1235
+ const m = creds.authorization.match(/^Bearer IGT:2:(.+)$/);
1236
+ if (m && m[1]) {
1237
+ try {
1238
+ const json = Buffer.from(m[1], 'base64').toString('utf8');
1239
+ const parsed = JSON.parse(json);
1240
+ if (parsed && typeof parsed === 'object') {
1241
+ if (parsed.ds_user_id) bearerDsUserId = String(parsed.ds_user_id);
1242
+ if (parsed.sessionid) bearerSessionId = String(parsed.sessionid);
1243
+ }
1244
+ } catch (e2) {
1245
+ // ignore decode errors
1246
+ }
1247
+ }
1248
+ }
1249
+ } catch (e) {
1250
+ // ignore bearer parse error
1251
+ }
1252
+
1253
+
1230
1254
  // Primary identifiers
1231
1255
  let userIdFromState = null;
1232
1256
  try {
@@ -1235,8 +1259,8 @@ async function extractStateData(igState) {
1235
1259
  if (!userIdFromState) {
1236
1260
  userIdFromState = igState.userId || igState.user_id || null;
1237
1261
  }
1238
- const dsUserIdFromCookies = cookieFields.dsUserIdCookie || igState.dsUserId || igState.ds_user_id || null;
1239
- const sessionIdFromCookies = cookieFields.sessionIdCookie || null;
1262
+ const dsUserIdFromCookies = cookieFields.dsUserIdCookie || igState.dsUserId || igState.ds_user_id || bearerDsUserId || null;
1263
+ const sessionIdFromCookies = cookieFields.sessionIdCookie || bearerSessionId || null;
1240
1264
  const csrfFromCookies = cookieFields.csrfTokenCookie || null;
1241
1265
  const midFromCookies = cookieFields.midCookie || igState.mid || null;
1242
1266
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-insta-private-api-mqt",
3
- "version": "1.3.80",
3
+ "version": "1.3.82",
4
4
  "description": "Complete Instagram MQTT protocol with FULL Featured REALTIME And api Rest All in one project .",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {