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.
package/dist/core/state.js
CHANGED
|
@@ -168,10 +168,7 @@ class State {
|
|
|
168
168
|
|
|
169
169
|
extractCookieValue(key) {
|
|
170
170
|
const cookie = this.extractCookie(key);
|
|
171
|
-
|
|
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
|
-
|
|
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('
|
|
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
|
|
56
|
-
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
|
-
|
|
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
|
-
'
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
|
|
100
|
-
'
|
|
101
|
-
'
|
|
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
|
-
'
|
|
111
|
-
|
|
112
|
-
'
|
|
113
|
-
'
|
|
114
|
-
'
|
|
115
|
-
'
|
|
116
|
-
'
|
|
117
|
-
'
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
'
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
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