nodejs-insta-private-api-mqt 1.3.87 → 1.3.88
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.
|
@@ -45,7 +45,7 @@ class AccountRepository extends Repository {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
//
|
|
48
|
+
// 1) Try straight headers for IG-Set-Authorization
|
|
49
49
|
for (const key of Object.keys(headers)) {
|
|
50
50
|
const val = headers[key];
|
|
51
51
|
if (!val) continue;
|
|
@@ -60,7 +60,6 @@ class AccountRepository extends Repository {
|
|
|
60
60
|
return token;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
// Some servers include the header value inside a JSON-like string in other headers
|
|
64
63
|
if (typeof val === 'string' && val.includes('Bearer IGT:2:')) {
|
|
65
64
|
const token = this._findBearerInString(val);
|
|
66
65
|
if (token) {
|
|
@@ -120,10 +119,9 @@ class AccountRepository extends Repository {
|
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
// 2) Check response.body for common fields
|
|
122
|
+
// 2) Check response.body for common fields / embedded layout/login_response
|
|
124
123
|
const body = response.body;
|
|
125
124
|
if (body) {
|
|
126
|
-
// If body has headers string (some responses embed headers JSON as a string)
|
|
127
125
|
if (body.headers && typeof body.headers === 'string') {
|
|
128
126
|
const token = this._findBearerInString(body.headers);
|
|
129
127
|
if (token) {
|
|
@@ -135,22 +133,20 @@ class AccountRepository extends Repository {
|
|
|
135
133
|
}
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
// If body contains a layout object or string, stringify and search it
|
|
139
136
|
try {
|
|
140
137
|
let layoutStr = null;
|
|
141
138
|
if (body.layout) {
|
|
142
|
-
layoutStr =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
: JSON.stringify(body.layout);
|
|
139
|
+
layoutStr = typeof body.layout === 'string'
|
|
140
|
+
? body.layout
|
|
141
|
+
: JSON.stringify(body.layout);
|
|
146
142
|
} else if (body?.layout?.bloks_payload) {
|
|
147
143
|
layoutStr = JSON.stringify(body.layout.bloks_payload);
|
|
148
144
|
} else if (body?.bloks_payload) {
|
|
149
145
|
layoutStr = JSON.stringify(body.bloks_payload);
|
|
150
|
-
}
|
|
151
|
-
if (!layoutStr && typeof body === 'string') {
|
|
146
|
+
} else if (typeof body === 'string') {
|
|
152
147
|
layoutStr = body;
|
|
153
148
|
}
|
|
149
|
+
|
|
154
150
|
if (layoutStr) {
|
|
155
151
|
const token = this._findBearerInString(layoutStr);
|
|
156
152
|
if (token) {
|
|
@@ -161,12 +157,11 @@ class AccountRepository extends Repository {
|
|
|
161
157
|
return token;
|
|
162
158
|
}
|
|
163
159
|
|
|
164
|
-
//
|
|
160
|
+
// Embedded login_response JSON string
|
|
165
161
|
const loginResponseMatch = layoutStr.match(
|
|
166
162
|
/"login_response"\s*:\s*"(\\?{.*?\\?})"/s
|
|
167
163
|
);
|
|
168
164
|
if (loginResponseMatch) {
|
|
169
|
-
// Unescape JSON string
|
|
170
165
|
let jsonStr = loginResponseMatch[1];
|
|
171
166
|
try {
|
|
172
167
|
jsonStr = jsonStr
|
|
@@ -174,23 +169,19 @@ class AccountRepository extends Repository {
|
|
|
174
169
|
.replace(/\\n/g, '');
|
|
175
170
|
const parsed = JSON.parse(jsonStr);
|
|
176
171
|
if (parsed && parsed.headers) {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (token) {
|
|
181
|
-
this.client.state.authorization = token;
|
|
172
|
+
const t = this._findBearerInString(parsed.headers);
|
|
173
|
+
if (t) {
|
|
174
|
+
this.client.state.authorization = t;
|
|
182
175
|
try {
|
|
183
176
|
this.client.state.updateAuthorization();
|
|
184
177
|
} catch (e) {}
|
|
185
|
-
return
|
|
178
|
+
return t;
|
|
186
179
|
}
|
|
187
180
|
}
|
|
188
|
-
} catch (e) {
|
|
189
|
-
// ignore parse errors
|
|
190
|
-
}
|
|
181
|
+
} catch (e) {}
|
|
191
182
|
}
|
|
192
183
|
|
|
193
|
-
//
|
|
184
|
+
// Encryption key id + pub key in layout
|
|
194
185
|
const encKeyIdMatch =
|
|
195
186
|
layoutStr.match(
|
|
196
187
|
/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i
|
|
@@ -202,6 +193,7 @@ class AccountRepository extends Repository {
|
|
|
202
193
|
this.client.state.passwordEncryptionKeyId =
|
|
203
194
|
parseInt(encKeyIdMatch[1]);
|
|
204
195
|
}
|
|
196
|
+
|
|
205
197
|
const encPubKeyMatch =
|
|
206
198
|
layoutStr.match(
|
|
207
199
|
/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
@@ -214,15 +206,13 @@ class AccountRepository extends Repository {
|
|
|
214
206
|
encPubKeyMatch[1];
|
|
215
207
|
}
|
|
216
208
|
|
|
217
|
-
//
|
|
209
|
+
// User / pk id
|
|
218
210
|
const dsIdMatch =
|
|
219
211
|
layoutStr.match(
|
|
220
212
|
/ig-set-ig-u-ds-user-id[\\"\s:]+(\d+)/i
|
|
221
213
|
) ||
|
|
222
214
|
layoutStr.match(/ig-u-ds-user-id[\\"\s:]+(\d+)/i) ||
|
|
223
|
-
layoutStr.match(
|
|
224
|
-
/"strong_id__"\s*:\s*"(\d+)"/i
|
|
225
|
-
) ||
|
|
215
|
+
layoutStr.match(/"strong_id__"\s*:\s*"(\d+)"/i) ||
|
|
226
216
|
layoutStr.match(/"pk_id"\s*:\s*"(\d+)"/i) ||
|
|
227
217
|
layoutStr.match(/"pk"\s*:\s*(\d+)/i);
|
|
228
218
|
if (dsIdMatch) {
|
|
@@ -254,11 +244,8 @@ class AccountRepository extends Repository {
|
|
|
254
244
|
this.client.state.mid = midMatch[1];
|
|
255
245
|
}
|
|
256
246
|
}
|
|
257
|
-
} catch (e) {
|
|
258
|
-
// ignore
|
|
259
|
-
}
|
|
247
|
+
} catch (e) {}
|
|
260
248
|
|
|
261
|
-
// 3) If body.logged_in_user exists and we have authorization already, return it
|
|
262
249
|
if (body.logged_in_user && this.client.state.authorization) {
|
|
263
250
|
return this.client.state.authorization;
|
|
264
251
|
}
|
|
@@ -267,15 +254,12 @@ class AccountRepository extends Repository {
|
|
|
267
254
|
return null;
|
|
268
255
|
}
|
|
269
256
|
|
|
270
|
-
// Normalize token string: accept either raw token or "Bearer IGT:2:..." and return trimmed token string
|
|
271
257
|
_normalizeTokenString(val) {
|
|
272
258
|
if (!val || typeof val !== 'string') return null;
|
|
273
259
|
|
|
274
|
-
// If header already contains "Bearer IGT:2:..."
|
|
275
260
|
const bearer = this._findBearerInString(val);
|
|
276
261
|
if (bearer) return bearer;
|
|
277
262
|
|
|
278
|
-
// If header contains JSON with IG-Set-Authorization field
|
|
279
263
|
try {
|
|
280
264
|
const maybeJson = JSON.parse(val);
|
|
281
265
|
if (maybeJson['IG-Set-Authorization']) {
|
|
@@ -283,19 +267,14 @@ class AccountRepository extends Repository {
|
|
|
283
267
|
maybeJson['IG-Set-Authorization']
|
|
284
268
|
);
|
|
285
269
|
}
|
|
286
|
-
} catch (e) {
|
|
287
|
-
// not JSON
|
|
288
|
-
}
|
|
270
|
+
} catch (e) {}
|
|
289
271
|
|
|
290
|
-
// fallback: return trimmed value
|
|
291
272
|
return val.trim();
|
|
292
273
|
}
|
|
293
274
|
|
|
294
|
-
// Find bearer token in arbitrary string using multiple patterns
|
|
295
275
|
_findBearerInString(str) {
|
|
296
276
|
if (!str || typeof str !== 'string') return null;
|
|
297
277
|
|
|
298
|
-
// Patterns to match the Bearer token in many possible encodings/escapes
|
|
299
278
|
const bearerPatterns = [
|
|
300
279
|
/Bearer IGT:2:[A-Za-z0-9+\/=]+/,
|
|
301
280
|
/Bearer\s+IGT:2:[A-Za-z0-9+\/=]+/,
|
|
@@ -309,14 +288,12 @@ class AccountRepository extends Repository {
|
|
|
309
288
|
for (const pattern of bearerPatterns) {
|
|
310
289
|
const m = str.match(pattern);
|
|
311
290
|
if (m) {
|
|
312
|
-
// If capturing group present, prefer it
|
|
313
291
|
if (m[1]) {
|
|
314
292
|
const token = m[1].includes('Bearer IGT:2:')
|
|
315
293
|
? m[1]
|
|
316
294
|
: `Bearer IGT:2:${m[1]}`;
|
|
317
295
|
return token.replace(/\?"/g, '').trim();
|
|
318
296
|
}
|
|
319
|
-
// Otherwise m[0] contains the full token
|
|
320
297
|
return m[0].replace(/\?"/g, '').trim();
|
|
321
298
|
}
|
|
322
299
|
}
|
|
@@ -324,8 +301,35 @@ class AccountRepository extends Repository {
|
|
|
324
301
|
return null;
|
|
325
302
|
}
|
|
326
303
|
|
|
304
|
+
/**
|
|
305
|
+
* NEW helper: resolve a valid User-Agent from state / request / deviceString
|
|
306
|
+
* so it works even if scriptul tău nu setează explicit state.userAgent.
|
|
307
|
+
*/
|
|
308
|
+
_resolveUserAgent() {
|
|
309
|
+
const state = this.client.state || {};
|
|
310
|
+
const req = this.client.request || {};
|
|
311
|
+
|
|
312
|
+
const candidates = [
|
|
313
|
+
state.userAgent,
|
|
314
|
+
state.appUserAgent,
|
|
315
|
+
req.userAgent,
|
|
316
|
+
state.deviceString,
|
|
317
|
+
].filter(Boolean);
|
|
318
|
+
|
|
319
|
+
let userAgent = candidates.length > 0 ? candidates[0] : null;
|
|
320
|
+
|
|
321
|
+
if (!userAgent) {
|
|
322
|
+
// Fallback – Pixel 9 Pro style UA (nu mai e Xiaomi)
|
|
323
|
+
userAgent =
|
|
324
|
+
'Instagram 371.0.0.0.23 Android (35/15; 505dpi; 1440x3120; google; Pixel 9 Pro; pixel9pro; qcom; ro_RO; 703217507)';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Sincronizăm în state ca să fie disponibil peste tot
|
|
328
|
+
this.client.state.userAgent = userAgent;
|
|
329
|
+
return userAgent;
|
|
330
|
+
}
|
|
331
|
+
|
|
327
332
|
// Ensure we have a valid csrftoken cookie (and mid) before sensitive endpoints.
|
|
328
|
-
// Instagram typically sets csrftoken via /api/v1/si/fetch_headers/.
|
|
329
333
|
async ensureCsrfToken() {
|
|
330
334
|
const cookieToken = this.client.state.cookieCsrfToken;
|
|
331
335
|
if (
|
|
@@ -339,15 +343,12 @@ class AccountRepository extends Repository {
|
|
|
339
343
|
return cookieToken;
|
|
340
344
|
}
|
|
341
345
|
|
|
342
|
-
// Warmup endpoint that returns/set cookies (csrftoken, mid, etc.)
|
|
343
346
|
try {
|
|
344
347
|
await this.client.request.send({
|
|
345
348
|
method: 'GET',
|
|
346
349
|
url: `/api/v1/si/fetch_headers/?challenge_type=signup&guid=${this.client.state.uuid}`,
|
|
347
350
|
});
|
|
348
|
-
} catch (e) {
|
|
349
|
-
// ignore warmup failures
|
|
350
|
-
}
|
|
351
|
+
} catch (e) {}
|
|
351
352
|
|
|
352
353
|
const token = this.client.state.cookieCsrfToken;
|
|
353
354
|
if (
|
|
@@ -365,9 +366,8 @@ class AccountRepository extends Repository {
|
|
|
365
366
|
}
|
|
366
367
|
|
|
367
368
|
/**
|
|
368
|
-
* Internal:
|
|
369
|
-
*
|
|
370
|
-
* This is best-effort only and never throws to the caller.
|
|
369
|
+
* Internal: pre-login Bloks OAuth token fetch, imită exact apelul oficial
|
|
370
|
+
* com.bloks.www.caa.login.oauth.token.fetch.async
|
|
371
371
|
*/
|
|
372
372
|
async _prefetchOauthTokenForLogin(username) {
|
|
373
373
|
if (!username) return null;
|
|
@@ -375,7 +375,6 @@ class AccountRepository extends Repository {
|
|
|
375
375
|
try {
|
|
376
376
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
377
377
|
|
|
378
|
-
// Device identifiers – keep them consistent with the main login flow
|
|
379
378
|
const androidDeviceId =
|
|
380
379
|
this.client.state.androidDeviceId ||
|
|
381
380
|
this.client.state.deviceId ||
|
|
@@ -404,7 +403,6 @@ class AccountRepository extends Repository {
|
|
|
404
403
|
`${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
405
404
|
);
|
|
406
405
|
|
|
407
|
-
// AAC object as seen in real traffic: timestamp + UUID + random token
|
|
408
406
|
const aacInitTimestamp =
|
|
409
407
|
nowSec - Math.floor(Math.random() * 50);
|
|
410
408
|
const aacjid = crypto.randomUUID
|
|
@@ -460,9 +458,7 @@ class AccountRepository extends Repository {
|
|
|
460
458
|
|
|
461
459
|
const lang = this.client.state.language || 'ro_RO';
|
|
462
460
|
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
463
|
-
const userAgent =
|
|
464
|
-
this.client.state.userAgent ||
|
|
465
|
-
'Instagram 371.0.0.0.23 Android (30/11; 320dpi; 720x1449; Xiaomi/Redmi; M2006C3MNG; angelican; mt6765; ro_RO; 703217507)';
|
|
461
|
+
const userAgent = this._resolveUserAgent();
|
|
466
462
|
|
|
467
463
|
const timezoneOffset =
|
|
468
464
|
typeof this.client.state.timezoneOffset === 'number'
|
|
@@ -474,7 +470,6 @@ class AccountRepository extends Repository {
|
|
|
474
470
|
'content-type':
|
|
475
471
|
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
476
472
|
'ig-intended-user-id': '0',
|
|
477
|
-
// Critical: matches the captured request header
|
|
478
473
|
priority: 'u=3',
|
|
479
474
|
|
|
480
475
|
'x-bloks-is-layout-rtl': 'false',
|
|
@@ -556,7 +551,6 @@ class AccountRepository extends Repository {
|
|
|
556
551
|
headers: bloksHeaders,
|
|
557
552
|
});
|
|
558
553
|
|
|
559
|
-
// Optional debug dump so you can inspect the exact server response.
|
|
560
554
|
try {
|
|
561
555
|
const fs = require('fs');
|
|
562
556
|
const path = require('path');
|
|
@@ -584,13 +578,10 @@ class AccountRepository extends Repository {
|
|
|
584
578
|
JSON.stringify(debugPayload, null, 2),
|
|
585
579
|
'utf8'
|
|
586
580
|
);
|
|
587
|
-
} catch (e) {
|
|
588
|
-
// Never break login on debug error
|
|
589
|
-
}
|
|
581
|
+
} catch (e) {}
|
|
590
582
|
|
|
591
583
|
return response.body;
|
|
592
584
|
} catch (e) {
|
|
593
|
-
// Completely swallow any errors; this step should never break login.
|
|
594
585
|
return null;
|
|
595
586
|
}
|
|
596
587
|
}
|
|
@@ -618,19 +609,13 @@ class AccountRepository extends Repository {
|
|
|
618
609
|
|
|
619
610
|
const { encrypted, time } = this.encryptPassword(password);
|
|
620
611
|
|
|
621
|
-
// optional CSRF warmup (like app does before login)
|
|
622
612
|
await this.ensureCsrfToken();
|
|
623
613
|
|
|
624
|
-
// Pre-login Bloks OAuth token fetch (mirrors captured request).
|
|
625
|
-
// This helps the flow look closer to the real app, but is best-effort only.
|
|
626
614
|
try {
|
|
627
615
|
await this._prefetchOauthTokenForLogin(username);
|
|
628
|
-
} catch (e) {
|
|
629
|
-
// Ignore any errors from the prefetch step.
|
|
630
|
-
}
|
|
616
|
+
} catch (e) {}
|
|
631
617
|
|
|
632
618
|
return this.requestWithRetry(async () => {
|
|
633
|
-
// ====== client_input_params (mirror of real traffic with password) ======
|
|
634
619
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
635
620
|
const aacInitTimestamp =
|
|
636
621
|
nowSec - Math.floor(Math.random() * 50);
|
|
@@ -717,7 +702,6 @@ class AccountRepository extends Repository {
|
|
|
717
702
|
contact_point: username,
|
|
718
703
|
};
|
|
719
704
|
|
|
720
|
-
// ====== server_params (mirror after reverse engineering) ======
|
|
721
705
|
const waterfallId = crypto.randomUUID
|
|
722
706
|
? crypto.randomUUID()
|
|
723
707
|
: require('uuid').v4();
|
|
@@ -783,12 +767,9 @@ class AccountRepository extends Repository {
|
|
|
783
767
|
styles_id: 'instagram',
|
|
784
768
|
});
|
|
785
769
|
|
|
786
|
-
// ====== HEADERS – modelled after real traffic ======
|
|
787
770
|
const lang = this.client.state.language || 'ro_RO';
|
|
788
771
|
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
789
|
-
const userAgent =
|
|
790
|
-
this.client.state.userAgent ||
|
|
791
|
-
'Instagram 371.0.0.0.23 Android (30/11; 320dpi; 720x1449; Xiaomi/Redmi; M2006C3MNG; angelican; mt6765; ro_RO; 703217507)';
|
|
772
|
+
const userAgent = this._resolveUserAgent();
|
|
792
773
|
|
|
793
774
|
const bloksHeaders = {
|
|
794
775
|
'accept-language': acceptLanguage,
|
|
@@ -887,7 +868,7 @@ class AccountRepository extends Repository {
|
|
|
887
868
|
headers: bloksHeaders,
|
|
888
869
|
});
|
|
889
870
|
|
|
890
|
-
//
|
|
871
|
+
// DEBUG login response
|
|
891
872
|
try {
|
|
892
873
|
const fs = require('fs');
|
|
893
874
|
const path = require('path');
|
|
@@ -915,14 +896,10 @@ class AccountRepository extends Repository {
|
|
|
915
896
|
JSON.stringify(debugPayload, null, 2),
|
|
916
897
|
'utf8'
|
|
917
898
|
);
|
|
918
|
-
} catch (e) {
|
|
919
|
-
// don't break login on debug failure
|
|
920
|
-
}
|
|
921
|
-
// === END DEBUG LOGIN ===
|
|
899
|
+
} catch (e) {}
|
|
922
900
|
|
|
923
901
|
const body = response.body;
|
|
924
902
|
|
|
925
|
-
// Immediately attempt to extract and save authorization token from the response
|
|
926
903
|
this._extractAndSaveAuthorization(response);
|
|
927
904
|
|
|
928
905
|
if (body && body.two_factor_required) {
|
|
@@ -956,7 +933,6 @@ class AccountRepository extends Repository {
|
|
|
956
933
|
? body.layout
|
|
957
934
|
: JSON.stringify(body.layout);
|
|
958
935
|
|
|
959
|
-
// If layout contains two-factor markers, parse and throw
|
|
960
936
|
if (
|
|
961
937
|
layoutStr.includes('two_factor_required') ||
|
|
962
938
|
layoutStr.includes('"two_factor_info"')
|
|
@@ -1000,7 +976,6 @@ class AccountRepository extends Repository {
|
|
|
1000
976
|
throw err;
|
|
1001
977
|
}
|
|
1002
978
|
|
|
1003
|
-
// Additional attempt to extract token from layout string
|
|
1004
979
|
const tokenFromLayout =
|
|
1005
980
|
this._findBearerInString(layoutStr);
|
|
1006
981
|
if (tokenFromLayout) {
|
|
@@ -1011,7 +986,6 @@ class AccountRepository extends Repository {
|
|
|
1011
986
|
} catch (e) {}
|
|
1012
987
|
}
|
|
1013
988
|
|
|
1014
|
-
// Extract pk (user id) from layout if present
|
|
1015
989
|
const pkPatterns = [
|
|
1016
990
|
/\\"pk\\":\s*(\d+)/,
|
|
1017
991
|
/"pk":\s*(\d+)/,
|
|
@@ -1032,7 +1006,6 @@ class AccountRepository extends Repository {
|
|
|
1032
1006
|
}
|
|
1033
1007
|
}
|
|
1034
1008
|
|
|
1035
|
-
// Extract IG-Set-WWW-Claim if present
|
|
1036
1009
|
const wwwClaimPatterns = [
|
|
1037
1010
|
/IG-Set-WWW-Claim[\\"\s:]+([a-f0-9]+)/i,
|
|
1038
1011
|
/ig-set-www-claim[\\"\s:]+([a-f0-9]+)/i,
|
|
@@ -1048,43 +1021,41 @@ class AccountRepository extends Repository {
|
|
|
1048
1021
|
}
|
|
1049
1022
|
}
|
|
1050
1023
|
|
|
1051
|
-
|
|
1052
|
-
const encKeyIdMatch =
|
|
1024
|
+
const encKeyIdMatch2 =
|
|
1053
1025
|
layoutStr.match(
|
|
1054
1026
|
/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i
|
|
1055
1027
|
) ||
|
|
1056
1028
|
layoutStr.match(
|
|
1057
1029
|
/password.encryption.key.id[\\"\s:]+(\d+)/i
|
|
1058
1030
|
);
|
|
1059
|
-
if (
|
|
1031
|
+
if (encKeyIdMatch2) {
|
|
1060
1032
|
this.client.state.passwordEncryptionKeyId =
|
|
1061
|
-
parseInt(
|
|
1033
|
+
parseInt(encKeyIdMatch2[1]);
|
|
1062
1034
|
}
|
|
1063
1035
|
|
|
1064
|
-
const
|
|
1036
|
+
const encPubKeyMatch2 =
|
|
1065
1037
|
layoutStr.match(
|
|
1066
1038
|
/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
1067
1039
|
) ||
|
|
1068
1040
|
layoutStr.match(
|
|
1069
1041
|
/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
1070
1042
|
);
|
|
1071
|
-
if (
|
|
1043
|
+
if (encPubKeyMatch2) {
|
|
1072
1044
|
this.client.state.passwordEncryptionPubKey =
|
|
1073
|
-
|
|
1045
|
+
encPubKeyMatch2[1];
|
|
1074
1046
|
}
|
|
1075
1047
|
|
|
1076
|
-
const
|
|
1048
|
+
const midMatch2 =
|
|
1077
1049
|
layoutStr.match(
|
|
1078
1050
|
/ig-set-x-mid[\\"\s:]+([A-Za-z0-9+\/=_-]+)/i
|
|
1079
1051
|
) ||
|
|
1080
1052
|
layoutStr.match(
|
|
1081
1053
|
/\\"x-mid\\"[\\"\s:]+([A-Za-z0-9+\/=_-]+)/i
|
|
1082
1054
|
);
|
|
1083
|
-
if (
|
|
1084
|
-
this.client.state.mid =
|
|
1055
|
+
if (midMatch2) {
|
|
1056
|
+
this.client.state.mid = midMatch2[1];
|
|
1085
1057
|
}
|
|
1086
1058
|
|
|
1087
|
-
// If layout contains a direct logged_in_user JSON, extract and return it
|
|
1088
1059
|
const loginResponsePatterns = [
|
|
1089
1060
|
/\\"logged_in_user\\":\{[^}]*\\"pk\\":(\d+)[^}]*\\"username\\":\\"([^"\\]+)\\"/,
|
|
1090
1061
|
/"logged_in_user":\{[^}]*"pk":(\d+)[^}]*"username":"([^"]+)"/,
|
|
@@ -1106,7 +1077,6 @@ class AccountRepository extends Repository {
|
|
|
1106
1077
|
}
|
|
1107
1078
|
}
|
|
1108
1079
|
|
|
1109
|
-
// If we have extracted a pk and we also have authorization, try to fetch current user
|
|
1110
1080
|
if (
|
|
1111
1081
|
extractedPk &&
|
|
1112
1082
|
this.client.state.authorization
|
|
@@ -1130,19 +1100,16 @@ class AccountRepository extends Repository {
|
|
|
1130
1100
|
}
|
|
1131
1101
|
}
|
|
1132
1102
|
|
|
1133
|
-
// If server returned logged_in_user directly in body
|
|
1134
1103
|
if (body && body.logged_in_user) {
|
|
1135
1104
|
const lu = body.logged_in_user;
|
|
1136
1105
|
if (lu.pk) {
|
|
1137
1106
|
this.client.state.cookieUserId = String(lu.pk);
|
|
1138
1107
|
this.client.state._userId = String(lu.pk);
|
|
1139
1108
|
}
|
|
1140
|
-
// Ensure authorization is saved if present in response headers
|
|
1141
1109
|
this._extractAndSaveAuthorization(response);
|
|
1142
1110
|
return lu;
|
|
1143
1111
|
}
|
|
1144
1112
|
|
|
1145
|
-
// If we already have authorization, try to fetch current user to populate state
|
|
1146
1113
|
if (this.client.state.authorization) {
|
|
1147
1114
|
try {
|
|
1148
1115
|
const userInfo = await this.currentUser();
|
|
@@ -1154,7 +1121,6 @@ class AccountRepository extends Repository {
|
|
|
1154
1121
|
}
|
|
1155
1122
|
return user;
|
|
1156
1123
|
} catch (e) {
|
|
1157
|
-
// fallback: return raw body
|
|
1158
1124
|
return body;
|
|
1159
1125
|
}
|
|
1160
1126
|
}
|
|
@@ -1184,7 +1150,6 @@ class AccountRepository extends Repository {
|
|
|
1184
1150
|
phone_id: this.client.state.phoneId,
|
|
1185
1151
|
}),
|
|
1186
1152
|
});
|
|
1187
|
-
// Save authorization if present in two-factor response
|
|
1188
1153
|
this._extractAndSaveAuthorization(response);
|
|
1189
1154
|
return response.body;
|
|
1190
1155
|
});
|
|
@@ -1572,18 +1537,6 @@ class AccountRepository extends Repository {
|
|
|
1572
1537
|
}
|
|
1573
1538
|
|
|
1574
1539
|
static generateAttestParams(state) {
|
|
1575
|
-
// Emulate Instagram's keystore attestation as closely as possible:
|
|
1576
|
-
// - version: 2
|
|
1577
|
-
// - type: "keystore"
|
|
1578
|
-
// - errors: [0]
|
|
1579
|
-
// - challenge_nonce: random base64url
|
|
1580
|
-
// - signed_nonce: ECDSA signature over the nonce
|
|
1581
|
-
// - key_hash: sha256(spki(publicKey))
|
|
1582
|
-
// - certificate_chain: 4 PEM certificates concatenated (leaf + 2 intermediates + root)
|
|
1583
|
-
//
|
|
1584
|
-
// NOTE: This is *not* a real hardware-backed attestation chain, but it mirrors
|
|
1585
|
-
// the structure of the official app very closely so the server sees the same
|
|
1586
|
-
// shape: a single attestation object with a 4-certificate chain.
|
|
1587
1540
|
const challengeNonce = crypto
|
|
1588
1541
|
.randomBytes(24)
|
|
1589
1542
|
.toString('base64url');
|
|
@@ -1593,7 +1546,6 @@ class AccountRepository extends Repository {
|
|
|
1593
1546
|
namedCurve: 'prime256v1',
|
|
1594
1547
|
}
|
|
1595
1548
|
);
|
|
1596
|
-
// Sign the challenge nonce with the private key (simulating TEE signing).
|
|
1597
1549
|
const signedData = crypto.sign(
|
|
1598
1550
|
null,
|
|
1599
1551
|
Buffer.from(challengeNonce),
|
|
@@ -1609,7 +1561,6 @@ class AccountRepository extends Repository {
|
|
|
1609
1561
|
.update(publicKeyDer)
|
|
1610
1562
|
.digest('hex');
|
|
1611
1563
|
|
|
1612
|
-
// Build a 4-certificate chain (leaf + 2 intermediates + root)
|
|
1613
1564
|
const leafCertPem =
|
|
1614
1565
|
AccountRepository._generateSelfSignedCert(
|
|
1615
1566
|
privateKey,
|
package/package.json
CHANGED