nodejs-insta-private-api-mqt 1.4.4 → 1.4.5
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.
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
const Repository = require('../core/repository');
|
|
2
2
|
const crypto = require('crypto');
|
|
3
3
|
|
|
4
|
+
// ─── DEBUG helper ────────────────────────────────────────────────────────────
|
|
5
|
+
// Set env DEBUG_IG=true to enable disk writes.
|
|
6
|
+
// In production leave it undefined / false.
|
|
7
|
+
const DEBUG_IG = process.env.DEBUG_IG === 'true';
|
|
8
|
+
|
|
9
|
+
function debugWrite(filename, payload) {
|
|
10
|
+
if (!DEBUG_IG) return;
|
|
11
|
+
try {
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const debugDir = path.join(process.cwd(), 'authinfo_instagram');
|
|
15
|
+
try { fs.mkdirSync(debugDir, { recursive: true }); } catch (e) {}
|
|
16
|
+
fs.writeFileSync(
|
|
17
|
+
path.join(debugDir, filename),
|
|
18
|
+
JSON.stringify(payload, null, 2),
|
|
19
|
+
'utf8'
|
|
20
|
+
);
|
|
21
|
+
} catch (e) {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ─── Local IP helper ──────────────────────────────────────────────────────────
|
|
25
|
+
// Detects the first non-loopback IP of the machine for x-fb-network-properties.
|
|
26
|
+
function getLocalIp() {
|
|
27
|
+
try {
|
|
28
|
+
const os = require('os');
|
|
29
|
+
const ifaces = os.networkInterfaces();
|
|
30
|
+
for (const name of Object.keys(ifaces)) {
|
|
31
|
+
for (const iface of ifaces[name]) {
|
|
32
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
33
|
+
return iface.address;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
return '192.168.1.100'; // reasonable fallback
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ─── Network properties generator ────────────────────────────────────────────
|
|
42
|
+
// Dynamically generates x-fb-network-properties header, no hardcoded VPN.
|
|
43
|
+
function buildNetworkProperties() {
|
|
44
|
+
const localIp = getLocalIp();
|
|
45
|
+
// If IP is 10.x.x.x or 172.16-31.x.x.x it may be a real VPN — keep prefix
|
|
46
|
+
const isVpnRange =
|
|
47
|
+
/^10\./.test(localIp) ||
|
|
48
|
+
/^172\.(1[6-9]|2\d|3[01])\./.test(localIp);
|
|
49
|
+
const prefix = isVpnRange ? 'VPN;Validated' : 'Validated';
|
|
50
|
+
return `${prefix};LocalAddrs=/${localIp},;`;
|
|
51
|
+
}
|
|
52
|
+
|
|
4
53
|
class AccountRepository extends Repository {
|
|
5
54
|
constructor(client) {
|
|
6
55
|
super(client);
|
|
@@ -9,8 +58,7 @@ class AccountRepository extends Repository {
|
|
|
9
58
|
|
|
10
59
|
async requestWithRetry(requestFn, retries = 0) {
|
|
11
60
|
try {
|
|
12
|
-
|
|
13
|
-
return result;
|
|
61
|
+
return await requestFn();
|
|
14
62
|
} catch (error) {
|
|
15
63
|
const shouldRetry =
|
|
16
64
|
(error.data?.error_type === 'server_error' ||
|
|
@@ -26,18 +74,12 @@ class AccountRepository extends Repository {
|
|
|
26
74
|
}
|
|
27
75
|
|
|
28
76
|
/**
|
|
29
|
-
* Helper:
|
|
30
|
-
*
|
|
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().
|
|
77
|
+
* Helper: extracts Bearer IGT token from headers / body / layout.
|
|
78
|
+
* When found, saves to state and calls updateAuthorization().
|
|
36
79
|
*/
|
|
37
80
|
_extractAndSaveAuthorization(response) {
|
|
38
81
|
if (!response) return null;
|
|
39
82
|
|
|
40
|
-
// Normalize headers (case-insensitive)
|
|
41
83
|
const headers = {};
|
|
42
84
|
if (response.headers && typeof response.headers === 'object') {
|
|
43
85
|
for (const k of Object.keys(response.headers)) {
|
|
@@ -45,7 +87,7 @@ class AccountRepository extends Repository {
|
|
|
45
87
|
}
|
|
46
88
|
}
|
|
47
89
|
|
|
48
|
-
// 1)
|
|
90
|
+
// 1) Header IG-Set-Authorization
|
|
49
91
|
for (const key of Object.keys(headers)) {
|
|
50
92
|
const val = headers[key];
|
|
51
93
|
if (!val) continue;
|
|
@@ -54,9 +96,7 @@ class AccountRepository extends Repository {
|
|
|
54
96
|
const token = this._normalizeTokenString(val);
|
|
55
97
|
if (token) {
|
|
56
98
|
this.client.state.authorization = token;
|
|
57
|
-
try {
|
|
58
|
-
this.client.state.updateAuthorization();
|
|
59
|
-
} catch (e) {}
|
|
99
|
+
try { this.client.state.updateAuthorization(); } catch (e) {}
|
|
60
100
|
return token;
|
|
61
101
|
}
|
|
62
102
|
}
|
|
@@ -64,15 +104,13 @@ class AccountRepository extends Repository {
|
|
|
64
104
|
const token = this._findBearerInString(val);
|
|
65
105
|
if (token) {
|
|
66
106
|
this.client.state.authorization = token;
|
|
67
|
-
try {
|
|
68
|
-
this.client.state.updateAuthorization();
|
|
69
|
-
} catch (e) {}
|
|
107
|
+
try { this.client.state.updateAuthorization(); } catch (e) {}
|
|
70
108
|
return token;
|
|
71
109
|
}
|
|
72
110
|
}
|
|
73
111
|
}
|
|
74
112
|
|
|
75
|
-
//
|
|
113
|
+
// IG-U headers
|
|
76
114
|
const dsUserIdHeader =
|
|
77
115
|
headers['ig-set-ig-u-ds-user-id'] ||
|
|
78
116
|
headers['ig-u-ds-user-id'] ||
|
|
@@ -80,9 +118,8 @@ class AccountRepository extends Repository {
|
|
|
80
118
|
if (dsUserIdHeader) {
|
|
81
119
|
const m = String(dsUserIdHeader).match(/(\d{3,})/);
|
|
82
120
|
if (m) {
|
|
83
|
-
|
|
84
|
-
this.client.state.
|
|
85
|
-
this.client.state._userId = uid;
|
|
121
|
+
this.client.state.cookieUserId = m[1];
|
|
122
|
+
this.client.state._userId = m[1];
|
|
86
123
|
}
|
|
87
124
|
}
|
|
88
125
|
|
|
@@ -92,9 +129,7 @@ class AccountRepository extends Repository {
|
|
|
92
129
|
headers['x-ig-set-ig-u-rur'];
|
|
93
130
|
if (rurHeader) {
|
|
94
131
|
const rur = String(rurHeader).trim().replace(/^"|"$/g, '');
|
|
95
|
-
if (rur)
|
|
96
|
-
this.client.state.igURur = rur;
|
|
97
|
-
}
|
|
132
|
+
if (rur) this.client.state.igURur = rur;
|
|
98
133
|
}
|
|
99
134
|
|
|
100
135
|
const wwwClaimHeader =
|
|
@@ -103,9 +138,7 @@ class AccountRepository extends Repository {
|
|
|
103
138
|
headers['ig-u-www-claim'];
|
|
104
139
|
if (wwwClaimHeader) {
|
|
105
140
|
const claim = String(wwwClaimHeader).trim().replace(/^"|"$/g, '');
|
|
106
|
-
if (claim)
|
|
107
|
-
this.client.state.igWWWClaim = claim;
|
|
108
|
-
}
|
|
141
|
+
if (claim) this.client.state.igWWWClaim = claim;
|
|
109
142
|
}
|
|
110
143
|
|
|
111
144
|
const midHeader =
|
|
@@ -114,21 +147,17 @@ class AccountRepository extends Repository {
|
|
|
114
147
|
headers['ig-set-mid'];
|
|
115
148
|
if (midHeader) {
|
|
116
149
|
const mid = String(midHeader).trim().replace(/^"|"$/g, '');
|
|
117
|
-
if (mid)
|
|
118
|
-
this.client.state.mid = mid;
|
|
119
|
-
}
|
|
150
|
+
if (mid) this.client.state.mid = mid;
|
|
120
151
|
}
|
|
121
152
|
|
|
122
|
-
// 2)
|
|
153
|
+
// 2) body
|
|
123
154
|
const body = response.body;
|
|
124
155
|
if (body) {
|
|
125
156
|
if (body.headers && typeof body.headers === 'string') {
|
|
126
157
|
const token = this._findBearerInString(body.headers);
|
|
127
158
|
if (token) {
|
|
128
159
|
this.client.state.authorization = token;
|
|
129
|
-
try {
|
|
130
|
-
this.client.state.updateAuthorization();
|
|
131
|
-
} catch (e) {}
|
|
160
|
+
try { this.client.state.updateAuthorization(); } catch (e) {}
|
|
132
161
|
return token;
|
|
133
162
|
}
|
|
134
163
|
}
|
|
@@ -139,8 +168,6 @@ class AccountRepository extends Repository {
|
|
|
139
168
|
layoutStr = typeof body.layout === 'string'
|
|
140
169
|
? body.layout
|
|
141
170
|
: JSON.stringify(body.layout);
|
|
142
|
-
} else if (body?.layout?.bloks_payload) {
|
|
143
|
-
layoutStr = JSON.stringify(body.layout.bloks_payload);
|
|
144
171
|
} else if (body?.bloks_payload) {
|
|
145
172
|
layoutStr = JSON.stringify(body.bloks_payload);
|
|
146
173
|
} else if (typeof body === 'string') {
|
|
@@ -151,9 +178,7 @@ class AccountRepository extends Repository {
|
|
|
151
178
|
const token = this._findBearerInString(layoutStr);
|
|
152
179
|
if (token) {
|
|
153
180
|
this.client.state.authorization = token;
|
|
154
|
-
try {
|
|
155
|
-
this.client.state.updateAuthorization();
|
|
156
|
-
} catch (e) {}
|
|
181
|
+
try { this.client.state.updateAuthorization(); } catch (e) {}
|
|
157
182
|
return token;
|
|
158
183
|
}
|
|
159
184
|
|
|
@@ -172,45 +197,31 @@ class AccountRepository extends Repository {
|
|
|
172
197
|
const t = this._findBearerInString(parsed.headers);
|
|
173
198
|
if (t) {
|
|
174
199
|
this.client.state.authorization = t;
|
|
175
|
-
try {
|
|
176
|
-
this.client.state.updateAuthorization();
|
|
177
|
-
} catch (e) {}
|
|
200
|
+
try { this.client.state.updateAuthorization(); } catch (e) {}
|
|
178
201
|
return t;
|
|
179
202
|
}
|
|
180
203
|
}
|
|
181
204
|
} catch (e) {}
|
|
182
205
|
}
|
|
183
206
|
|
|
184
|
-
// Encryption
|
|
207
|
+
// Encryption keys
|
|
185
208
|
const encKeyIdMatch =
|
|
186
|
-
layoutStr.match(
|
|
187
|
-
|
|
188
|
-
) ||
|
|
189
|
-
layoutStr.match(
|
|
190
|
-
/password.encryption.key.id[\\"\s:]+(\d+)/i
|
|
191
|
-
);
|
|
209
|
+
layoutStr.match(/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i) ||
|
|
210
|
+
layoutStr.match(/password.encryption.key.id[\\"\s:]+(\d+)/i);
|
|
192
211
|
if (encKeyIdMatch) {
|
|
193
|
-
this.client.state.passwordEncryptionKeyId =
|
|
194
|
-
parseInt(encKeyIdMatch[1]);
|
|
212
|
+
this.client.state.passwordEncryptionKeyId = parseInt(encKeyIdMatch[1]);
|
|
195
213
|
}
|
|
196
214
|
|
|
197
215
|
const encPubKeyMatch =
|
|
198
|
-
layoutStr.match(
|
|
199
|
-
|
|
200
|
-
) ||
|
|
201
|
-
layoutStr.match(
|
|
202
|
-
/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
203
|
-
);
|
|
216
|
+
layoutStr.match(/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i) ||
|
|
217
|
+
layoutStr.match(/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i);
|
|
204
218
|
if (encPubKeyMatch) {
|
|
205
|
-
this.client.state.passwordEncryptionPubKey =
|
|
206
|
-
encPubKeyMatch[1];
|
|
219
|
+
this.client.state.passwordEncryptionPubKey = encPubKeyMatch[1];
|
|
207
220
|
}
|
|
208
221
|
|
|
209
|
-
// User
|
|
222
|
+
// User ID
|
|
210
223
|
const dsIdMatch =
|
|
211
|
-
layoutStr.match(
|
|
212
|
-
/ig-set-ig-u-ds-user-id[\\"\s:]+(\d+)/i
|
|
213
|
-
) ||
|
|
224
|
+
layoutStr.match(/ig-set-ig-u-ds-user-id[\\"\s:]+(\d+)/i) ||
|
|
214
225
|
layoutStr.match(/ig-u-ds-user-id[\\"\s:]+(\d+)/i) ||
|
|
215
226
|
layoutStr.match(/"strong_id__"\s*:\s*"(\d+)"/i) ||
|
|
216
227
|
layoutStr.match(/"pk_id"\s*:\s*"(\d+)"/i) ||
|
|
@@ -222,27 +233,15 @@ class AccountRepository extends Repository {
|
|
|
222
233
|
}
|
|
223
234
|
|
|
224
235
|
const rurMatch =
|
|
225
|
-
layoutStr.match(
|
|
226
|
-
/ig-set-ig-u-rur[\\"\s:]+"([^"\\]+)/i
|
|
227
|
-
) ||
|
|
236
|
+
layoutStr.match(/ig-set-ig-u-rur[\\"\s:]+"([^"\\]+)/i) ||
|
|
228
237
|
layoutStr.match(/ig-u-rur[\\"\s:]+"([^"\\]+)/i);
|
|
229
|
-
if (rurMatch)
|
|
230
|
-
this.client.state.igURur = rurMatch[1];
|
|
231
|
-
}
|
|
238
|
+
if (rurMatch) this.client.state.igURur = rurMatch[1];
|
|
232
239
|
|
|
233
|
-
const wwwClaimMatch = layoutStr.match(
|
|
234
|
-
|
|
235
|
-
);
|
|
236
|
-
if (wwwClaimMatch) {
|
|
237
|
-
this.client.state.igWWWClaim = wwwClaimMatch[1];
|
|
238
|
-
}
|
|
240
|
+
const wwwClaimMatch = layoutStr.match(/x-ig-set-www-claim[\\"\s:]+"([^"\\]+)/i);
|
|
241
|
+
if (wwwClaimMatch) this.client.state.igWWWClaim = wwwClaimMatch[1];
|
|
239
242
|
|
|
240
|
-
const midMatch = layoutStr.match(
|
|
241
|
-
|
|
242
|
-
);
|
|
243
|
-
if (midMatch) {
|
|
244
|
-
this.client.state.mid = midMatch[1];
|
|
245
|
-
}
|
|
243
|
+
const midMatch = layoutStr.match(/"mid"\s*:\s*"([^"\\]+)"/i);
|
|
244
|
+
if (midMatch) this.client.state.mid = midMatch[1];
|
|
246
245
|
}
|
|
247
246
|
} catch (e) {}
|
|
248
247
|
|
|
@@ -256,19 +255,14 @@ class AccountRepository extends Repository {
|
|
|
256
255
|
|
|
257
256
|
_normalizeTokenString(val) {
|
|
258
257
|
if (!val || typeof val !== 'string') return null;
|
|
259
|
-
|
|
260
258
|
const bearer = this._findBearerInString(val);
|
|
261
259
|
if (bearer) return bearer;
|
|
262
|
-
|
|
263
260
|
try {
|
|
264
261
|
const maybeJson = JSON.parse(val);
|
|
265
262
|
if (maybeJson['IG-Set-Authorization']) {
|
|
266
|
-
return this._findBearerInString(
|
|
267
|
-
maybeJson['IG-Set-Authorization']
|
|
268
|
-
);
|
|
263
|
+
return this._findBearerInString(maybeJson['IG-Set-Authorization']);
|
|
269
264
|
}
|
|
270
265
|
} catch (e) {}
|
|
271
|
-
|
|
272
266
|
return val.trim();
|
|
273
267
|
}
|
|
274
268
|
|
|
@@ -297,13 +291,13 @@ class AccountRepository extends Repository {
|
|
|
297
291
|
return m[0].replace(/\?"/g, '').trim();
|
|
298
292
|
}
|
|
299
293
|
}
|
|
300
|
-
|
|
301
294
|
return null;
|
|
302
295
|
}
|
|
303
296
|
|
|
304
297
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
298
|
+
* Resolves a valid User-Agent from state / realistic fallback.
|
|
299
|
+
* IMPORTANT: IG version, build number and bloksVersionId must be
|
|
300
|
+
* in sync. Update all three when Instagram updates.
|
|
307
301
|
*/
|
|
308
302
|
_resolveUserAgent() {
|
|
309
303
|
const state = this.client.state || {};
|
|
@@ -319,27 +313,20 @@ class AccountRepository extends Repository {
|
|
|
319
313
|
let userAgent = candidates.length > 0 ? candidates[0] : null;
|
|
320
314
|
|
|
321
315
|
if (!userAgent) {
|
|
322
|
-
//
|
|
316
|
+
// FIX: real existing version — 415.0.0.36.76 (build 580610226)
|
|
317
|
+
// Keep in sync with bloksVersionId below.
|
|
323
318
|
userAgent =
|
|
324
|
-
'Instagram
|
|
319
|
+
'Instagram 415.0.0.36.76 Android (35/15; 480dpi; 1280x2856; Google; Pixel 9 Pro; caiman; google; en_US; 580610226)';
|
|
325
320
|
}
|
|
326
321
|
|
|
327
|
-
// Sincronizăm în state ca să fie disponibil peste tot
|
|
328
322
|
this.client.state.userAgent = userAgent;
|
|
329
323
|
return userAgent;
|
|
330
324
|
}
|
|
331
325
|
|
|
332
|
-
// Ensure we have a valid csrftoken cookie (and mid) before sensitive endpoints.
|
|
333
326
|
async ensureCsrfToken() {
|
|
334
327
|
const cookieToken = this.client.state.cookieCsrfToken;
|
|
335
|
-
if (
|
|
336
|
-
cookieToken
|
|
337
|
-
cookieToken !== 'missing' &&
|
|
338
|
-
cookieToken !== 'pending'
|
|
339
|
-
) {
|
|
340
|
-
try {
|
|
341
|
-
this.client.state.csrfToken = cookieToken;
|
|
342
|
-
} catch {}
|
|
328
|
+
if (cookieToken && cookieToken !== 'missing' && cookieToken !== 'pending') {
|
|
329
|
+
try { this.client.state.csrfToken = cookieToken; } catch {}
|
|
343
330
|
return cookieToken;
|
|
344
331
|
}
|
|
345
332
|
|
|
@@ -351,24 +338,13 @@ class AccountRepository extends Repository {
|
|
|
351
338
|
} catch (e) {}
|
|
352
339
|
|
|
353
340
|
const token = this.client.state.cookieCsrfToken;
|
|
354
|
-
if (
|
|
355
|
-
token
|
|
356
|
-
token !== 'missing' &&
|
|
357
|
-
token !== 'pending'
|
|
358
|
-
) {
|
|
359
|
-
try {
|
|
360
|
-
this.client.state.csrfToken = token;
|
|
361
|
-
} catch {}
|
|
341
|
+
if (token && token !== 'missing' && token !== 'pending') {
|
|
342
|
+
try { this.client.state.csrfToken = token; } catch {}
|
|
362
343
|
return token;
|
|
363
344
|
}
|
|
364
|
-
|
|
365
345
|
return null;
|
|
366
346
|
}
|
|
367
347
|
|
|
368
|
-
/**
|
|
369
|
-
* Internal: pre-login Bloks OAuth token fetch, imită exact apelul oficial
|
|
370
|
-
* com.bloks.www.caa.login.oauth.token.fetch.async
|
|
371
|
-
*/
|
|
372
348
|
async _prefetchOauthTokenForLogin(username) {
|
|
373
349
|
if (!username) return null;
|
|
374
350
|
|
|
@@ -383,16 +359,12 @@ class AccountRepository extends Repository {
|
|
|
383
359
|
const familyDeviceId =
|
|
384
360
|
this.client.state.phoneId ||
|
|
385
361
|
this.client.state.familyDeviceId ||
|
|
386
|
-
(crypto.randomUUID
|
|
387
|
-
? crypto.randomUUID()
|
|
388
|
-
: require('uuid').v4());
|
|
362
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
389
363
|
|
|
390
364
|
const qeDeviceId =
|
|
391
365
|
this.client.state.deviceId ||
|
|
392
366
|
this.client.state.qeDeviceId ||
|
|
393
|
-
(crypto.randomUUID
|
|
394
|
-
? crypto.randomUUID()
|
|
395
|
-
: require('uuid').v4());
|
|
367
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
396
368
|
|
|
397
369
|
const waterfallId = crypto.randomUUID
|
|
398
370
|
? crypto.randomUUID()
|
|
@@ -403,23 +375,15 @@ class AccountRepository extends Repository {
|
|
|
403
375
|
`${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
404
376
|
);
|
|
405
377
|
|
|
406
|
-
const aacInitTimestamp =
|
|
407
|
-
nowSec - Math.floor(Math.random() * 50);
|
|
378
|
+
const aacInitTimestamp = nowSec - Math.floor(Math.random() * 50);
|
|
408
379
|
const aacjid = crypto.randomUUID
|
|
409
380
|
? crypto.randomUUID()
|
|
410
381
|
: require('uuid').v4();
|
|
411
|
-
const aaccs = crypto
|
|
412
|
-
.randomBytes(32)
|
|
413
|
-
.toString('base64')
|
|
414
|
-
.replace(/=/g, '');
|
|
382
|
+
const aaccs = crypto.randomBytes(32).toString('base64').replace(/=/g, '');
|
|
415
383
|
|
|
416
384
|
const clientInputParams = {
|
|
417
385
|
username_input: username,
|
|
418
|
-
aac: JSON.stringify({
|
|
419
|
-
aac_init_timestamp: aacInitTimestamp,
|
|
420
|
-
aacjid,
|
|
421
|
-
aaccs,
|
|
422
|
-
}),
|
|
386
|
+
aac: JSON.stringify({ aac_init_timestamp: aacInitTimestamp, aacjid, aaccs }),
|
|
423
387
|
lois_settings: { lois_token: '' },
|
|
424
388
|
cloud_trust_token: null,
|
|
425
389
|
zero_balance_state: '',
|
|
@@ -447,6 +411,7 @@ class AccountRepository extends Repository {
|
|
|
447
411
|
server_params: serverParams,
|
|
448
412
|
});
|
|
449
413
|
|
|
414
|
+
// FIX: bloksVersionId kept in sync with IG version from _resolveUserAgent
|
|
450
415
|
const bloksVersionId =
|
|
451
416
|
this.client.state.bloksVersionId ||
|
|
452
417
|
'5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
|
|
@@ -459,19 +424,19 @@ class AccountRepository extends Repository {
|
|
|
459
424
|
const lang = this.client.state.language || 'ro_RO';
|
|
460
425
|
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
461
426
|
const userAgent = this._resolveUserAgent();
|
|
462
|
-
|
|
463
427
|
const timezoneOffset =
|
|
464
428
|
typeof this.client.state.timezoneOffset === 'number'
|
|
465
429
|
? this.client.state.timezoneOffset
|
|
466
430
|
: 7200;
|
|
467
431
|
|
|
432
|
+
// FIX: dynamic network properties
|
|
433
|
+
const networkProps = buildNetworkProperties();
|
|
434
|
+
|
|
468
435
|
const bloksHeaders = {
|
|
469
436
|
'accept-language': acceptLanguage,
|
|
470
|
-
'content-type':
|
|
471
|
-
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
437
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
472
438
|
'ig-intended-user-id': '0',
|
|
473
439
|
priority: 'u=3',
|
|
474
|
-
|
|
475
440
|
'x-bloks-is-layout-rtl': 'false',
|
|
476
441
|
'x-bloks-prism-ax-base-colors-enabled': 'false',
|
|
477
442
|
'x-bloks-prism-button-version': 'CONTROL',
|
|
@@ -479,53 +444,35 @@ class AccountRepository extends Repository {
|
|
|
479
444
|
'x-bloks-prism-font-enabled': 'false',
|
|
480
445
|
'x-bloks-prism-indigo-link-version': '0',
|
|
481
446
|
'x-bloks-version-id': bloksVersionId,
|
|
482
|
-
|
|
483
447
|
'x-ig-android-id': androidDeviceId,
|
|
484
448
|
'x-ig-device-id': qeDeviceId,
|
|
485
449
|
'x-ig-family-device-id': familyDeviceId,
|
|
486
450
|
'x-ig-timezone-offset': String(timezoneOffset),
|
|
487
|
-
'x-ig-app-id': String(
|
|
488
|
-
this.client.state.fbAnalyticsApplicationId ||
|
|
489
|
-
'567067343352427'
|
|
490
|
-
),
|
|
451
|
+
'x-ig-app-id': String(this.client.state.fbAnalyticsApplicationId || '567067343352427'),
|
|
491
452
|
'x-ig-app-locale': lang,
|
|
492
453
|
'x-ig-device-locale': lang,
|
|
493
454
|
'x-ig-mapped-locale': lang,
|
|
494
|
-
|
|
495
|
-
'x-ig-client-endpoint':
|
|
496
|
-
'com.bloks.www.caa.login.login_homepage',
|
|
455
|
+
'x-ig-client-endpoint': 'com.bloks.www.caa.login.login_homepage',
|
|
497
456
|
'x-ig-nav-chain':
|
|
498
457
|
'com.bloks.www.caa.login.login_homepage:com.bloks.www.caa.login.login_homepage:1:button:0:::0',
|
|
499
|
-
|
|
500
458
|
'x-ig-connection-type': 'WIFI',
|
|
501
459
|
'x-ig-capabilities': '3brTv10=',
|
|
502
460
|
'x-ig-www-claim': this.client.state.igWWWClaim || '0',
|
|
503
|
-
|
|
504
|
-
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
|
|
505
|
-
Math.random() * 1000
|
|
506
|
-
)
|
|
507
|
-
.toString()
|
|
508
|
-
.padStart(3, '0')}`,
|
|
461
|
+
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`,
|
|
509
462
|
'x-pigeon-session-id':
|
|
510
463
|
this.client.state.pigeonSessionId ||
|
|
511
464
|
`UFS-${crypto.randomBytes(16).toString('hex')}-2`,
|
|
512
|
-
|
|
513
465
|
'x-mid':
|
|
514
466
|
this.client.state.mid ||
|
|
515
467
|
`aZ${crypto.randomBytes(8).toString('hex')}`,
|
|
516
468
|
'x-tigon-is-retry': 'False',
|
|
517
|
-
|
|
518
469
|
'x-fb-client-ip': 'True',
|
|
519
470
|
'x-fb-connection-type': 'WIFI',
|
|
520
471
|
'x-fb-server-cluster': 'True',
|
|
521
|
-
'x-fb-network-properties':
|
|
522
|
-
'VPN;Validated;LocalAddrs=/10.0.0.2,;',
|
|
472
|
+
'x-fb-network-properties': networkProps, // FIX: dynamic
|
|
523
473
|
'x-fb-request-analytics-tags': JSON.stringify({
|
|
524
474
|
network_tags: {
|
|
525
|
-
product: String(
|
|
526
|
-
this.client.state.fbAnalyticsApplicationId ||
|
|
527
|
-
'567067343352427'
|
|
528
|
-
),
|
|
475
|
+
product: String(this.client.state.fbAnalyticsApplicationId || '567067343352427'),
|
|
529
476
|
purpose: 'fetch',
|
|
530
477
|
surface: 'undefined',
|
|
531
478
|
request_category: 'api',
|
|
@@ -536,7 +483,6 @@ class AccountRepository extends Repository {
|
|
|
536
483
|
'IgApi: bloks/async_action/com.bloks.www.caa.login.oauth.token.fetch.async/',
|
|
537
484
|
'x-fb-http-engine': 'MNS/TCP',
|
|
538
485
|
'x-fb-rmd': 'state=URL_ELIGIBLE',
|
|
539
|
-
|
|
540
486
|
'user-agent': userAgent,
|
|
541
487
|
};
|
|
542
488
|
|
|
@@ -551,34 +497,12 @@ class AccountRepository extends Repository {
|
|
|
551
497
|
headers: bloksHeaders,
|
|
552
498
|
});
|
|
553
499
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
);
|
|
561
|
-
const debugFile = path.join(
|
|
562
|
-
debugDir,
|
|
563
|
-
'oauth-token-debug.json'
|
|
564
|
-
);
|
|
565
|
-
try {
|
|
566
|
-
fs.mkdirSync(debugDir, { recursive: true });
|
|
567
|
-
} catch (e) {}
|
|
568
|
-
|
|
569
|
-
const debugPayload = {
|
|
570
|
-
at: new Date().toISOString(),
|
|
571
|
-
statusCode:
|
|
572
|
-
response.statusCode || response.status || null,
|
|
573
|
-
headers: response.headers || null,
|
|
574
|
-
body: response.body || null,
|
|
575
|
-
};
|
|
576
|
-
fs.writeFileSync(
|
|
577
|
-
debugFile,
|
|
578
|
-
JSON.stringify(debugPayload, null, 2),
|
|
579
|
-
'utf8'
|
|
580
|
-
);
|
|
581
|
-
} catch (e) {}
|
|
500
|
+
debugWrite('oauth-token-debug.json', {
|
|
501
|
+
at: new Date().toISOString(),
|
|
502
|
+
statusCode: response.statusCode || response.status || null,
|
|
503
|
+
headers: response.headers || null,
|
|
504
|
+
body: response.body || null,
|
|
505
|
+
});
|
|
582
506
|
|
|
583
507
|
return response.body;
|
|
584
508
|
} catch (e) {
|
|
@@ -586,10 +510,6 @@ class AccountRepository extends Repository {
|
|
|
586
510
|
}
|
|
587
511
|
}
|
|
588
512
|
|
|
589
|
-
/**
|
|
590
|
-
* Step 1: launcher/mobileconfig to fetch mobile config + encryption keys.
|
|
591
|
-
* Best effort – errors are ignored.
|
|
592
|
-
*/
|
|
593
513
|
async _launcherMobileConfig(preLogin = true) {
|
|
594
514
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
595
515
|
const state = this.client.state || {};
|
|
@@ -602,16 +522,12 @@ class AccountRepository extends Repository {
|
|
|
602
522
|
const familyDeviceId =
|
|
603
523
|
state.phoneId ||
|
|
604
524
|
state.familyDeviceId ||
|
|
605
|
-
(crypto.randomUUID
|
|
606
|
-
? crypto.randomUUID()
|
|
607
|
-
: require('uuid').v4());
|
|
525
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
608
526
|
|
|
609
527
|
const qeDeviceId =
|
|
610
528
|
state.deviceId ||
|
|
611
529
|
state.qeDeviceId ||
|
|
612
|
-
(crypto.randomUUID
|
|
613
|
-
? crypto.randomUUID()
|
|
614
|
-
: require('uuid').v4());
|
|
530
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
615
531
|
|
|
616
532
|
const deviceUUID = state.uuid || qeDeviceId;
|
|
617
533
|
const userId = state.cookieUserId || '0';
|
|
@@ -623,11 +539,12 @@ class AccountRepository extends Repository {
|
|
|
623
539
|
const lang = state.language || 'ro_RO';
|
|
624
540
|
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
625
541
|
const userAgent = this._resolveUserAgent();
|
|
626
|
-
|
|
627
542
|
const timezoneOffset =
|
|
628
|
-
typeof state.timezoneOffset === 'number'
|
|
629
|
-
|
|
630
|
-
|
|
543
|
+
typeof state.timezoneOffset === 'number' ? state.timezoneOffset : 7200;
|
|
544
|
+
|
|
545
|
+
// FIX: salt-ids from state or fallback
|
|
546
|
+
const saltIds = state.igSaltIds || '220140399,332020310,974466465,974460658';
|
|
547
|
+
const networkProps = buildNetworkProperties();
|
|
631
548
|
|
|
632
549
|
const params = {
|
|
633
550
|
bool_opt_policy: '0',
|
|
@@ -636,8 +553,7 @@ class AccountRepository extends Repository {
|
|
|
636
553
|
client_context: '["opt,value_hash"]',
|
|
637
554
|
unit_type: '2',
|
|
638
555
|
use_case: 'STANDARD',
|
|
639
|
-
query_hash:
|
|
640
|
-
'f00b9d0869db3969378d8d06bfccb24b5ef078012c8e199cba961cd5dfedaa88',
|
|
556
|
+
query_hash: 'f00b9d0869db3969378d8d06bfccb24b5ef078012c8e199cba961cd5dfedaa88',
|
|
641
557
|
ts: String(nowSec),
|
|
642
558
|
_uid: userId,
|
|
643
559
|
device_id: deviceUUID,
|
|
@@ -648,10 +564,8 @@ class AccountRepository extends Repository {
|
|
|
648
564
|
|
|
649
565
|
const headers = {
|
|
650
566
|
'accept-language': acceptLanguage,
|
|
651
|
-
'content-type':
|
|
652
|
-
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
567
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
653
568
|
priority: 'u=3',
|
|
654
|
-
|
|
655
569
|
'x-bloks-is-layout-rtl': 'false',
|
|
656
570
|
'x-bloks-prism-ax-base-colors-enabled': 'false',
|
|
657
571
|
'x-bloks-prism-button-version': 'CONTROL',
|
|
@@ -659,28 +573,19 @@ class AccountRepository extends Repository {
|
|
|
659
573
|
'x-bloks-prism-font-enabled': 'false',
|
|
660
574
|
'x-bloks-prism-indigo-link-version': '0',
|
|
661
575
|
'x-bloks-version-id': bloksVersionId,
|
|
662
|
-
|
|
663
576
|
'x-fb-client-ip': 'True',
|
|
664
577
|
'x-fb-connection-type': 'WIFI',
|
|
665
578
|
'x-fb-server-cluster': 'True',
|
|
666
|
-
'x-fb-network-properties':
|
|
667
|
-
'VPN;Validated;LocalAddrs=/10.0.0.2,;',
|
|
579
|
+
'x-fb-network-properties': networkProps, // FIX: dynamic
|
|
668
580
|
'x-fb-http-engine': 'MNS/TCP',
|
|
669
581
|
'x-fb-rmd': 'state=URL_ELIGIBLE',
|
|
670
|
-
|
|
671
582
|
'x-ig-android-id': androidDeviceId,
|
|
672
|
-
'x-ig-app-id': String(
|
|
673
|
-
state.fbAnalyticsApplicationId || '567067343352427'
|
|
674
|
-
),
|
|
583
|
+
'x-ig-app-id': String(state.fbAnalyticsApplicationId || '567067343352427'),
|
|
675
584
|
'x-ig-app-locale': lang,
|
|
676
|
-
'x-ig-bandwidth-speed-kbps': (
|
|
677
|
-
Math.random() * 1500 +
|
|
678
|
-
800
|
|
679
|
-
).toFixed(3),
|
|
585
|
+
'x-ig-bandwidth-speed-kbps': (Math.random() * 1500 + 800).toFixed(3),
|
|
680
586
|
'x-ig-bandwidth-totalbytes-b': '0',
|
|
681
587
|
'x-ig-bandwidth-totaltime-ms': '0',
|
|
682
|
-
'x-ig-client-endpoint':
|
|
683
|
-
'LockoutFragment:dogfooding_lockout',
|
|
588
|
+
'x-ig-client-endpoint': 'LockoutFragment:dogfooding_lockout',
|
|
684
589
|
'x-ig-capabilities': '3brTv10=',
|
|
685
590
|
'x-ig-connection-type': 'WIFI',
|
|
686
591
|
'x-ig-device-id': deviceUUID,
|
|
@@ -688,26 +593,19 @@ class AccountRepository extends Repository {
|
|
|
688
593
|
'x-ig-device-locale': lang,
|
|
689
594
|
'x-ig-family-device-id': familyDeviceId,
|
|
690
595
|
'x-ig-mapped-locale': lang,
|
|
691
|
-
'x-ig-nav-chain':
|
|
692
|
-
|
|
693
|
-
'x-ig-salt-ids':
|
|
694
|
-
'220140399,332020310,974466465,974460658',
|
|
596
|
+
'x-ig-nav-chain': 'LockoutFragment:dogfooding_lockout:1:cold_start',
|
|
597
|
+
'x-ig-salt-ids': saltIds, // FIX: from state or fallback
|
|
695
598
|
'x-ig-timezone-offset': String(timezoneOffset),
|
|
696
599
|
'x-ig-www-claim': state.igWWWClaim || '0',
|
|
697
600
|
'x-mid':
|
|
698
601
|
state.mid ||
|
|
699
602
|
state.machineId ||
|
|
700
603
|
`aZ${crypto.randomBytes(8).toString('hex')}`,
|
|
701
|
-
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
|
|
702
|
-
Math.random() * 1000
|
|
703
|
-
)
|
|
704
|
-
.toString()
|
|
705
|
-
.padStart(3, '0')}`,
|
|
604
|
+
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`,
|
|
706
605
|
'x-pigeon-session-id':
|
|
707
606
|
state.pigeonSessionId ||
|
|
708
607
|
`UFS-${crypto.randomBytes(16).toString('hex')}-M`,
|
|
709
608
|
'x-tigon-is-retry': 'False',
|
|
710
|
-
|
|
711
609
|
'accept-encoding': 'gzip, deflate, br',
|
|
712
610
|
'user-agent': userAgent,
|
|
713
611
|
};
|
|
@@ -716,12 +614,8 @@ class AccountRepository extends Repository {
|
|
|
716
614
|
headers['ig-intended-user-id'] = String(state.cookieUserId);
|
|
717
615
|
headers['ig-u-ds-user-id'] = String(state.cookieUserId);
|
|
718
616
|
}
|
|
719
|
-
if (state.igURur)
|
|
720
|
-
|
|
721
|
-
}
|
|
722
|
-
if (state.authorization) {
|
|
723
|
-
headers['authorization'] = state.authorization;
|
|
724
|
-
}
|
|
617
|
+
if (state.igURur) headers['ig-u-rur'] = state.igURur;
|
|
618
|
+
if (state.authorization) headers['authorization'] = state.authorization;
|
|
725
619
|
|
|
726
620
|
try {
|
|
727
621
|
const response = await this.client.request.send({
|
|
@@ -731,7 +625,6 @@ class AccountRepository extends Repository {
|
|
|
731
625
|
headers,
|
|
732
626
|
});
|
|
733
627
|
|
|
734
|
-
// NEW: salvăm cheia de criptare a parolei direct din headers de la mobileconfig
|
|
735
628
|
try {
|
|
736
629
|
const mcHeaders = response.headers || {};
|
|
737
630
|
const mcLower = {};
|
|
@@ -739,10 +632,8 @@ class AccountRepository extends Repository {
|
|
|
739
632
|
mcLower[k.toLowerCase()] = mcHeaders[k];
|
|
740
633
|
}
|
|
741
634
|
|
|
742
|
-
const mcKeyIdStr =
|
|
743
|
-
|
|
744
|
-
const mcPubKey =
|
|
745
|
-
mcLower['ig-set-password-encryption-pub-key'];
|
|
635
|
+
const mcKeyIdStr = mcLower['ig-set-password-encryption-key-id'];
|
|
636
|
+
const mcPubKey = mcLower['ig-set-password-encryption-pub-key'];
|
|
746
637
|
|
|
747
638
|
if (mcKeyIdStr) {
|
|
748
639
|
const parsedKeyId = parseInt(mcKeyIdStr, 10);
|
|
@@ -750,39 +641,15 @@ class AccountRepository extends Repository {
|
|
|
750
641
|
state.passwordEncryptionKeyId = parsedKeyId;
|
|
751
642
|
}
|
|
752
643
|
}
|
|
753
|
-
|
|
754
|
-
if (mcPubKey) {
|
|
755
|
-
state.passwordEncryptionPubKey = mcPubKey;
|
|
756
|
-
}
|
|
644
|
+
if (mcPubKey) state.passwordEncryptionPubKey = mcPubKey;
|
|
757
645
|
} catch (e) {}
|
|
758
646
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
);
|
|
766
|
-
const debugFile = path.join(
|
|
767
|
-
debugDir,
|
|
768
|
-
'launcher-mobileconfig-debug.json'
|
|
769
|
-
);
|
|
770
|
-
try {
|
|
771
|
-
fs.mkdirSync(debugDir, { recursive: true });
|
|
772
|
-
} catch (e) {}
|
|
773
|
-
const debugPayload = {
|
|
774
|
-
at: new Date().toISOString(),
|
|
775
|
-
statusCode:
|
|
776
|
-
response.statusCode || response.status || null,
|
|
777
|
-
headers: response.headers || null,
|
|
778
|
-
body: response.body || null,
|
|
779
|
-
};
|
|
780
|
-
fs.writeFileSync(
|
|
781
|
-
debugFile,
|
|
782
|
-
JSON.stringify(debugPayload, null, 2),
|
|
783
|
-
'utf8'
|
|
784
|
-
);
|
|
785
|
-
} catch (e) {}
|
|
647
|
+
debugWrite('launcher-mobileconfig-debug.json', {
|
|
648
|
+
at: new Date().toISOString(),
|
|
649
|
+
statusCode: response.statusCode || response.status || null,
|
|
650
|
+
headers: response.headers || null,
|
|
651
|
+
body: response.body || null,
|
|
652
|
+
});
|
|
786
653
|
|
|
787
654
|
return response.body;
|
|
788
655
|
} catch (e) {
|
|
@@ -790,10 +657,6 @@ class AccountRepository extends Repository {
|
|
|
790
657
|
}
|
|
791
658
|
}
|
|
792
659
|
|
|
793
|
-
/**
|
|
794
|
-
* Step 2: Android Keystore attestation – moves x-ig-attest-params
|
|
795
|
-
* payload to dedicated endpoint /api/v1/attestation/create_android_keystore/.
|
|
796
|
-
*/
|
|
797
660
|
async _createAndroidKeystoreAttestation() {
|
|
798
661
|
const state = this.client.state || {};
|
|
799
662
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
@@ -806,30 +669,25 @@ class AccountRepository extends Repository {
|
|
|
806
669
|
const familyDeviceId =
|
|
807
670
|
state.phoneId ||
|
|
808
671
|
state.familyDeviceId ||
|
|
809
|
-
(crypto.randomUUID
|
|
810
|
-
? crypto.randomUUID()
|
|
811
|
-
: require('uuid').v4());
|
|
672
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
812
673
|
|
|
813
674
|
const qeDeviceId =
|
|
814
675
|
state.deviceId ||
|
|
815
676
|
state.qeDeviceId ||
|
|
816
|
-
(crypto.randomUUID
|
|
817
|
-
? crypto.randomUUID()
|
|
818
|
-
: require('uuid').v4());
|
|
677
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
819
678
|
|
|
820
679
|
const deviceUUID = state.uuid || qeDeviceId;
|
|
821
680
|
const userId = state.cookieUserId || '0';
|
|
822
681
|
|
|
823
|
-
|
|
824
|
-
|
|
682
|
+
// FIX: each cert in the chain has its own key pair
|
|
683
|
+
const attestParams = AccountRepository.generateAttestParams(state);
|
|
825
684
|
|
|
826
685
|
const lang = state.language || 'ro_RO';
|
|
827
686
|
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
828
687
|
const userAgent = this._resolveUserAgent();
|
|
829
688
|
const timezoneOffset =
|
|
830
|
-
typeof state.timezoneOffset === 'number'
|
|
831
|
-
|
|
832
|
-
: 7200;
|
|
689
|
+
typeof state.timezoneOffset === 'number' ? state.timezoneOffset : 7200;
|
|
690
|
+
const networkProps = buildNetworkProperties();
|
|
833
691
|
|
|
834
692
|
const params = {
|
|
835
693
|
_uid: userId,
|
|
@@ -842,29 +700,20 @@ class AccountRepository extends Repository {
|
|
|
842
700
|
|
|
843
701
|
const headers = {
|
|
844
702
|
'accept-language': acceptLanguage,
|
|
845
|
-
'content-type':
|
|
846
|
-
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
703
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
847
704
|
priority: 'u=3',
|
|
848
|
-
|
|
849
705
|
'x-ig-android-id': androidDeviceId,
|
|
850
706
|
'x-ig-device-id': qeDeviceId,
|
|
851
707
|
'x-ig-family-device-id': familyDeviceId,
|
|
852
708
|
'x-ig-timezone-offset': String(timezoneOffset),
|
|
853
|
-
'x-ig-app-id': String(
|
|
854
|
-
state.fbAnalyticsApplicationId || '567067343352427'
|
|
855
|
-
),
|
|
709
|
+
'x-ig-app-id': String(state.fbAnalyticsApplicationId || '567067343352427'),
|
|
856
710
|
'x-ig-app-locale': lang,
|
|
857
711
|
'x-ig-device-locale': lang,
|
|
858
712
|
'x-ig-mapped-locale': lang,
|
|
859
713
|
'x-ig-connection-type': 'WIFI',
|
|
860
714
|
'x-ig-capabilities': '3brTv10=',
|
|
861
715
|
'x-ig-www-claim': state.igWWWClaim || '0',
|
|
862
|
-
|
|
863
|
-
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
|
|
864
|
-
Math.random() * 1000
|
|
865
|
-
)
|
|
866
|
-
.toString()
|
|
867
|
-
.padStart(3, '0')}`,
|
|
716
|
+
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`,
|
|
868
717
|
'x-pigeon-session-id':
|
|
869
718
|
state.pigeonSessionId ||
|
|
870
719
|
`UFS-${crypto.randomBytes(16).toString('hex')}-A`,
|
|
@@ -872,28 +721,19 @@ class AccountRepository extends Repository {
|
|
|
872
721
|
state.mid ||
|
|
873
722
|
state.machineId ||
|
|
874
723
|
`aZ${crypto.randomBytes(8).toString('hex')}`,
|
|
875
|
-
|
|
876
724
|
'x-fb-client-ip': 'True',
|
|
877
725
|
'x-fb-connection-type': 'WIFI',
|
|
878
726
|
'x-fb-server-cluster': 'True',
|
|
879
727
|
'x-fb-http-engine': 'MNS/TCP',
|
|
880
|
-
'x-fb-network-properties':
|
|
881
|
-
'VPN;Validated;LocalAddrs=/10.0.0.2,;',
|
|
728
|
+
'x-fb-network-properties': networkProps, // FIX: dynamic
|
|
882
729
|
'x-fb-rmd': 'state=URL_ELIGIBLE',
|
|
883
|
-
|
|
884
730
|
'accept-encoding': 'gzip, deflate, br',
|
|
885
731
|
'user-agent': userAgent,
|
|
886
732
|
};
|
|
887
733
|
|
|
888
|
-
if (state.cookieUserId)
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
if (state.authorization) {
|
|
892
|
-
headers['authorization'] = state.authorization;
|
|
893
|
-
}
|
|
894
|
-
if (state.igURur) {
|
|
895
|
-
headers['ig-u-rur'] = state.igURur;
|
|
896
|
-
}
|
|
734
|
+
if (state.cookieUserId) headers['ig-intended-user-id'] = String(state.cookieUserId);
|
|
735
|
+
if (state.authorization) headers['authorization'] = state.authorization;
|
|
736
|
+
if (state.igURur) headers['ig-u-rur'] = state.igURur;
|
|
897
737
|
|
|
898
738
|
try {
|
|
899
739
|
const response = await this.client.request.send({
|
|
@@ -903,33 +743,12 @@ class AccountRepository extends Repository {
|
|
|
903
743
|
headers,
|
|
904
744
|
});
|
|
905
745
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
);
|
|
913
|
-
const debugFile = path.join(
|
|
914
|
-
debugDir,
|
|
915
|
-
'attestation-debug.json'
|
|
916
|
-
);
|
|
917
|
-
try {
|
|
918
|
-
fs.mkdirSync(debugDir, { recursive: true });
|
|
919
|
-
} catch (e) {}
|
|
920
|
-
const debugPayload = {
|
|
921
|
-
at: new Date().toISOString(),
|
|
922
|
-
statusCode:
|
|
923
|
-
response.statusCode || response.status || null,
|
|
924
|
-
headers: response.headers || null,
|
|
925
|
-
body: response.body || null,
|
|
926
|
-
};
|
|
927
|
-
fs.writeFileSync(
|
|
928
|
-
debugFile,
|
|
929
|
-
JSON.stringify(debugPayload, null, 2),
|
|
930
|
-
'utf8'
|
|
931
|
-
);
|
|
932
|
-
} catch (e) {}
|
|
746
|
+
debugWrite('attestation-debug.json', {
|
|
747
|
+
at: new Date().toISOString(),
|
|
748
|
+
statusCode: response.statusCode || response.status || null,
|
|
749
|
+
headers: response.headers || null,
|
|
750
|
+
body: response.body || null,
|
|
751
|
+
});
|
|
933
752
|
|
|
934
753
|
return response.body;
|
|
935
754
|
} catch (e) {
|
|
@@ -937,11 +756,6 @@ class AccountRepository extends Repository {
|
|
|
937
756
|
}
|
|
938
757
|
}
|
|
939
758
|
|
|
940
|
-
/**
|
|
941
|
-
* Step 3 & 4 & 5 helpers: Terms of service preload, process client data,
|
|
942
|
-
* and (optional) phone number prefill.
|
|
943
|
-
* These are best-effort calls; failures are ignored.
|
|
944
|
-
*/
|
|
945
759
|
async _preloadTermsOfService() {
|
|
946
760
|
const state = this.client.state || {};
|
|
947
761
|
const lang = state.language || 'ro_RO';
|
|
@@ -952,29 +766,19 @@ class AccountRepository extends Repository {
|
|
|
952
766
|
'5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
|
|
953
767
|
|
|
954
768
|
const paramsJson = JSON.stringify({});
|
|
955
|
-
const bkClientContext = JSON.stringify({
|
|
956
|
-
bloks_version: bloksVersionId,
|
|
957
|
-
styles_id: 'instagram',
|
|
958
|
-
});
|
|
959
|
-
|
|
769
|
+
const bkClientContext = JSON.stringify({ bloks_version: bloksVersionId, styles_id: 'instagram' });
|
|
960
770
|
const headers = {
|
|
961
771
|
'accept-language': acceptLanguage,
|
|
962
|
-
'content-type':
|
|
963
|
-
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
772
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
964
773
|
priority: 'u=3',
|
|
965
774
|
'x-bloks-version-id': bloksVersionId,
|
|
966
775
|
'user-agent': userAgent,
|
|
967
776
|
};
|
|
968
|
-
|
|
969
777
|
try {
|
|
970
778
|
const response = await this.client.request.send({
|
|
971
779
|
method: 'POST',
|
|
972
780
|
url: '/api/v1/bloks/apps/com.bloks.www.caa.login.oxygen_preloads_terms_of_service/',
|
|
973
|
-
form: {
|
|
974
|
-
params: paramsJson,
|
|
975
|
-
bk_client_context: bkClientContext,
|
|
976
|
-
bloks_versioning_id: bloksVersionId,
|
|
977
|
-
},
|
|
781
|
+
form: { params: paramsJson, bk_client_context: bkClientContext, bloks_versioning_id: bloksVersionId },
|
|
978
782
|
headers,
|
|
979
783
|
});
|
|
980
784
|
return response.body;
|
|
@@ -992,32 +796,20 @@ class AccountRepository extends Repository {
|
|
|
992
796
|
state.bloksVersionId ||
|
|
993
797
|
'5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
|
|
994
798
|
|
|
995
|
-
const paramsJson = JSON.stringify({
|
|
996
|
-
|
|
997
|
-
});
|
|
998
|
-
const bkClientContext = JSON.stringify({
|
|
999
|
-
bloks_version: bloksVersionId,
|
|
1000
|
-
styles_id: 'instagram',
|
|
1001
|
-
});
|
|
1002
|
-
|
|
799
|
+
const paramsJson = JSON.stringify({ username_input: username || '' });
|
|
800
|
+
const bkClientContext = JSON.stringify({ bloks_version: bloksVersionId, styles_id: 'instagram' });
|
|
1003
801
|
const headers = {
|
|
1004
802
|
'accept-language': acceptLanguage,
|
|
1005
|
-
'content-type':
|
|
1006
|
-
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
803
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
1007
804
|
priority: 'u=3',
|
|
1008
805
|
'x-bloks-version-id': bloksVersionId,
|
|
1009
806
|
'user-agent': userAgent,
|
|
1010
807
|
};
|
|
1011
|
-
|
|
1012
808
|
try {
|
|
1013
809
|
const response = await this.client.request.send({
|
|
1014
810
|
method: 'POST',
|
|
1015
811
|
url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.login.process_client_data_and_redirect/',
|
|
1016
|
-
form: {
|
|
1017
|
-
params: paramsJson,
|
|
1018
|
-
bk_client_context: bkClientContext,
|
|
1019
|
-
bloks_versioning_id: bloksVersionId,
|
|
1020
|
-
},
|
|
812
|
+
form: { params: paramsJson, bk_client_context: bkClientContext, bloks_versioning_id: bloksVersionId },
|
|
1021
813
|
headers,
|
|
1022
814
|
});
|
|
1023
815
|
return response.body;
|
|
@@ -1036,29 +828,19 @@ class AccountRepository extends Repository {
|
|
|
1036
828
|
'5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
|
|
1037
829
|
|
|
1038
830
|
const paramsJson = JSON.stringify({});
|
|
1039
|
-
const bkClientContext = JSON.stringify({
|
|
1040
|
-
bloks_version: bloksVersionId,
|
|
1041
|
-
styles_id: 'instagram',
|
|
1042
|
-
});
|
|
1043
|
-
|
|
831
|
+
const bkClientContext = JSON.stringify({ bloks_version: bloksVersionId, styles_id: 'instagram' });
|
|
1044
832
|
const headers = {
|
|
1045
833
|
'accept-language': acceptLanguage,
|
|
1046
|
-
'content-type':
|
|
1047
|
-
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
834
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
1048
835
|
priority: 'u=3',
|
|
1049
836
|
'x-bloks-version-id': bloksVersionId,
|
|
1050
837
|
'user-agent': userAgent,
|
|
1051
838
|
};
|
|
1052
|
-
|
|
1053
839
|
try {
|
|
1054
840
|
const response = await this.client.request.send({
|
|
1055
841
|
method: 'POST',
|
|
1056
842
|
url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.phone.number.prefill.async.controller/',
|
|
1057
|
-
form: {
|
|
1058
|
-
params: paramsJson,
|
|
1059
|
-
bk_client_context: bkClientContext,
|
|
1060
|
-
bloks_versioning_id: bloksVersionId,
|
|
1061
|
-
},
|
|
843
|
+
form: { params: paramsJson, bk_client_context: bkClientContext, bloks_versioning_id: bloksVersionId },
|
|
1062
844
|
headers,
|
|
1063
845
|
});
|
|
1064
846
|
return response.body;
|
|
@@ -1069,10 +851,7 @@ class AccountRepository extends Repository {
|
|
|
1069
851
|
|
|
1070
852
|
async login(credentialsOrUsername, passwordArg) {
|
|
1071
853
|
let username, password;
|
|
1072
|
-
if (
|
|
1073
|
-
typeof credentialsOrUsername === 'object' &&
|
|
1074
|
-
credentialsOrUsername !== null
|
|
1075
|
-
) {
|
|
854
|
+
if (typeof credentialsOrUsername === 'object' && credentialsOrUsername !== null) {
|
|
1076
855
|
username = credentialsOrUsername.username;
|
|
1077
856
|
password = credentialsOrUsername.password;
|
|
1078
857
|
} else {
|
|
@@ -1080,57 +859,37 @@ class AccountRepository extends Repository {
|
|
|
1080
859
|
password = passwordArg;
|
|
1081
860
|
}
|
|
1082
861
|
|
|
1083
|
-
if (!username || !password)
|
|
1084
|
-
throw new Error('Username and password are required');
|
|
1085
|
-
}
|
|
862
|
+
if (!username || !password) throw new Error('Username and password are required');
|
|
1086
863
|
|
|
1087
|
-
// 0: ne asigurăm că avem csrf + cookies de bază
|
|
1088
864
|
await this.ensureCsrfToken();
|
|
1089
865
|
|
|
1090
|
-
|
|
1091
|
-
try {
|
|
1092
|
-
await this._launcherMobileConfig(true);
|
|
1093
|
-
} catch (e) {}
|
|
866
|
+
try { await this._launcherMobileConfig(true); } catch (e) {}
|
|
1094
867
|
|
|
1095
|
-
// Fallback: dacă mobileconfig nu a setat cheia, folosim mecanismul vechi de LOGIN_EXPERIMENTS
|
|
1096
868
|
if (!this.client.state.passwordEncryptionPubKey) {
|
|
1097
869
|
await this.syncLoginExperiments();
|
|
1098
870
|
}
|
|
1099
871
|
|
|
1100
|
-
//
|
|
1101
|
-
|
|
872
|
+
// FIX: if we still have no public key, throw error — do not send password in plaintext
|
|
873
|
+
if (!this.client.state.passwordEncryptionPubKey) {
|
|
874
|
+
throw new Error(
|
|
875
|
+
'Could not obtain the password encryption public key. ' +
|
|
876
|
+
'Check that mobileconfig and syncLoginExperiments return valid data.'
|
|
877
|
+
);
|
|
878
|
+
}
|
|
1102
879
|
|
|
1103
|
-
|
|
1104
|
-
try {
|
|
1105
|
-
await this._createAndroidKeystoreAttestation();
|
|
1106
|
-
} catch (e) {}
|
|
1107
|
-
try {
|
|
1108
|
-
await this._preloadTermsOfService();
|
|
1109
|
-
} catch (e) {}
|
|
1110
|
-
try {
|
|
1111
|
-
await this._processClientDataAndRedirect(username);
|
|
1112
|
-
} catch (e) {}
|
|
1113
|
-
try {
|
|
1114
|
-
await this._phoneNumberPrefill();
|
|
1115
|
-
} catch (e) {}
|
|
880
|
+
const { encrypted, time } = this.encryptPassword(password);
|
|
1116
881
|
|
|
1117
|
-
|
|
1118
|
-
try {
|
|
1119
|
-
|
|
1120
|
-
} catch (e) {}
|
|
882
|
+
try { await this._createAndroidKeystoreAttestation(); } catch (e) {}
|
|
883
|
+
try { await this._preloadTermsOfService(); } catch (e) {}
|
|
884
|
+
try { await this._processClientDataAndRedirect(username); } catch (e) {}
|
|
885
|
+
try { await this._phoneNumberPrefill(); } catch (e) {}
|
|
886
|
+
try { await this._prefetchOauthTokenForLogin(username); } catch (e) {}
|
|
1121
887
|
|
|
1122
|
-
// Step 7: real login
|
|
1123
888
|
return this.requestWithRetry(async () => {
|
|
1124
889
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
1125
|
-
const aacInitTimestamp =
|
|
1126
|
-
|
|
1127
|
-
const
|
|
1128
|
-
? crypto.randomUUID()
|
|
1129
|
-
: require('uuid').v4();
|
|
1130
|
-
const aaccs = crypto
|
|
1131
|
-
.randomBytes(32)
|
|
1132
|
-
.toString('base64')
|
|
1133
|
-
.replace(/=/g, '');
|
|
890
|
+
const aacInitTimestamp = nowSec - Math.floor(Math.random() * 50);
|
|
891
|
+
const aacjid = crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4();
|
|
892
|
+
const aaccs = crypto.randomBytes(32).toString('base64').replace(/=/g, '');
|
|
1134
893
|
|
|
1135
894
|
const androidDeviceId =
|
|
1136
895
|
this.client.state.androidDeviceId ||
|
|
@@ -1143,18 +902,13 @@ class AccountRepository extends Repository {
|
|
|
1143
902
|
const familyDeviceId =
|
|
1144
903
|
this.client.state.phoneId ||
|
|
1145
904
|
this.client.state.familyDeviceId ||
|
|
1146
|
-
(crypto.randomUUID
|
|
1147
|
-
? crypto.randomUUID()
|
|
1148
|
-
: require('uuid').v4());
|
|
905
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
1149
906
|
const qeDeviceId =
|
|
1150
907
|
this.client.state.deviceId ||
|
|
1151
908
|
this.client.state.qeDeviceId ||
|
|
1152
|
-
(crypto.randomUUID
|
|
1153
|
-
? crypto.randomUUID()
|
|
1154
|
-
: require('uuid').v4());
|
|
909
|
+
(crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4());
|
|
1155
910
|
|
|
1156
911
|
const accountsList = this.client.state.accountsList || [];
|
|
1157
|
-
|
|
1158
912
|
const flashCallPermissionStatus = {
|
|
1159
913
|
READ_PHONE_STATE: 'GRANTED',
|
|
1160
914
|
READ_CALL_LOG: 'GRANTED',
|
|
@@ -1162,11 +916,7 @@ class AccountRepository extends Repository {
|
|
|
1162
916
|
};
|
|
1163
917
|
|
|
1164
918
|
const clientInputParams = {
|
|
1165
|
-
aac: JSON.stringify({
|
|
1166
|
-
aac_init_timestamp: aacInitTimestamp,
|
|
1167
|
-
aacjid,
|
|
1168
|
-
aaccs,
|
|
1169
|
-
}),
|
|
919
|
+
aac: JSON.stringify({ aac_init_timestamp: aacInitTimestamp, aacjid, aaccs }),
|
|
1170
920
|
sim_phones: [],
|
|
1171
921
|
aymh_accounts: [],
|
|
1172
922
|
network_bssid: null,
|
|
@@ -1194,8 +944,7 @@ class AccountRepository extends Repository {
|
|
|
1194
944
|
machine_id: machineId,
|
|
1195
945
|
flash_call_permission_status: flashCallPermissionStatus,
|
|
1196
946
|
accounts_list: accountsList,
|
|
1197
|
-
gms_incoming_call_retriever_eligibility:
|
|
1198
|
-
'client_not_supported',
|
|
947
|
+
gms_incoming_call_retriever_eligibility: 'client_not_supported',
|
|
1199
948
|
family_device_id: familyDeviceId,
|
|
1200
949
|
fb_ig_device_id: [],
|
|
1201
950
|
device_emails: [],
|
|
@@ -1207,13 +956,9 @@ class AccountRepository extends Repository {
|
|
|
1207
956
|
contact_point: username,
|
|
1208
957
|
};
|
|
1209
958
|
|
|
1210
|
-
const waterfallId = crypto.randomUUID
|
|
1211
|
-
? crypto.randomUUID()
|
|
1212
|
-
: require('uuid').v4();
|
|
959
|
+
const waterfallId = crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4();
|
|
1213
960
|
const latencyMarkerId = 36707139;
|
|
1214
|
-
const latencyInstanceId = Number(
|
|
1215
|
-
`${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
1216
|
-
);
|
|
961
|
+
const latencyInstanceId = Number(`${Date.now()}${Math.floor(Math.random() * 1000)}`);
|
|
1217
962
|
|
|
1218
963
|
const serverParams = {
|
|
1219
964
|
should_trigger_override_login_2fa_action: 0,
|
|
@@ -1228,8 +973,7 @@ class AccountRepository extends Repository {
|
|
|
1228
973
|
is_platform_login: 0,
|
|
1229
974
|
INTERNAL__latency_qpl_marker_id: latencyMarkerId,
|
|
1230
975
|
is_from_aymh: 0,
|
|
1231
|
-
offline_experiment_group:
|
|
1232
|
-
'caa_iteration_v3_perf_ig_4',
|
|
976
|
+
offline_experiment_group: 'caa_iteration_v3_perf_ig_4',
|
|
1233
977
|
is_from_landing_page: 0,
|
|
1234
978
|
left_nav_button_action: 'NONE',
|
|
1235
979
|
password_text_input_id: 'z0jejq:194',
|
|
@@ -1240,10 +984,9 @@ class AccountRepository extends Repository {
|
|
|
1240
984
|
username_text_input_id: 'z0jejq:193',
|
|
1241
985
|
layered_homepage_experiment_group: null,
|
|
1242
986
|
device_id: androidDeviceId,
|
|
1243
|
-
login_surface: '
|
|
987
|
+
login_surface: 'login_home',
|
|
1244
988
|
INTERNAL__latency_qpl_instance_id: latencyInstanceId,
|
|
1245
|
-
reg_flow_source:
|
|
1246
|
-
'login_home_native_integration_point',
|
|
989
|
+
reg_flow_source: 'aymh_multi_profiles_native_integration_point',
|
|
1247
990
|
is_caa_perf_enabled: 1,
|
|
1248
991
|
credential_type: 'password',
|
|
1249
992
|
is_from_password_entry_page: 0,
|
|
@@ -1263,22 +1006,17 @@ class AccountRepository extends Repository {
|
|
|
1263
1006
|
this.client.state.bloksVersionId ||
|
|
1264
1007
|
'5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
|
|
1265
1008
|
|
|
1266
|
-
const bkClientContext = JSON.stringify({
|
|
1267
|
-
bloks_version: bloksVersionId,
|
|
1268
|
-
styles_id: 'instagram',
|
|
1269
|
-
});
|
|
1270
|
-
|
|
1009
|
+
const bkClientContext = JSON.stringify({ bloks_version: bloksVersionId, styles_id: 'instagram' });
|
|
1271
1010
|
const lang = this.client.state.language || 'ro_RO';
|
|
1272
1011
|
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
1273
1012
|
const userAgent = this._resolveUserAgent();
|
|
1013
|
+
const networkProps = buildNetworkProperties();
|
|
1274
1014
|
|
|
1275
1015
|
const bloksHeaders = {
|
|
1276
1016
|
'accept-language': acceptLanguage,
|
|
1277
|
-
'content-type':
|
|
1278
|
-
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
1017
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
1279
1018
|
'ig-intended-user-id': '0',
|
|
1280
1019
|
priority: 'u=3',
|
|
1281
|
-
|
|
1282
1020
|
'x-bloks-is-layout-rtl': 'false',
|
|
1283
1021
|
'x-bloks-prism-ax-base-colors-enabled': 'false',
|
|
1284
1022
|
'x-bloks-prism-button-version': 'CONTROL',
|
|
@@ -1286,19 +1024,14 @@ class AccountRepository extends Repository {
|
|
|
1286
1024
|
'x-bloks-prism-font-enabled': 'false',
|
|
1287
1025
|
'x-bloks-prism-indigo-link-version': '0',
|
|
1288
1026
|
'x-bloks-version-id': bloksVersionId,
|
|
1289
|
-
|
|
1290
1027
|
'x-fb-client-ip': 'True',
|
|
1291
|
-
'x-fb-connection-type': '
|
|
1028
|
+
'x-fb-connection-type': 'MOBILE.UNKNOWN',
|
|
1292
1029
|
'x-fb-friendly-name':
|
|
1293
1030
|
'IgApi: bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
|
|
1294
|
-
'x-fb-network-properties':
|
|
1295
|
-
'VPN;Validated;LocalAddrs=/10.0.0.2,;',
|
|
1031
|
+
'x-fb-network-properties': networkProps, // FIX: dynamic
|
|
1296
1032
|
'x-fb-request-analytics-tags': JSON.stringify({
|
|
1297
1033
|
network_tags: {
|
|
1298
|
-
product: String(
|
|
1299
|
-
this.client.state.fbAnalyticsApplicationId ||
|
|
1300
|
-
'567067343352427'
|
|
1301
|
-
),
|
|
1034
|
+
product: String(this.client.state.fbAnalyticsApplicationId || '567067343352427'),
|
|
1302
1035
|
purpose: 'fetch',
|
|
1303
1036
|
surface: 'undefined',
|
|
1304
1037
|
request_category: 'api',
|
|
@@ -1306,23 +1039,16 @@ class AccountRepository extends Repository {
|
|
|
1306
1039
|
},
|
|
1307
1040
|
}),
|
|
1308
1041
|
'x-fb-server-cluster': 'True',
|
|
1309
|
-
|
|
1310
1042
|
'x-ig-android-id': androidDeviceId,
|
|
1311
|
-
'x-ig-app-id': String(
|
|
1312
|
-
this.client.state.fbAnalyticsApplicationId ||
|
|
1313
|
-
'567067343352427'
|
|
1314
|
-
),
|
|
1043
|
+
'x-ig-app-id': String(this.client.state.fbAnalyticsApplicationId || '567067343352427'),
|
|
1315
1044
|
'x-ig-app-locale': lang,
|
|
1316
|
-
'x-ig-bandwidth-speed-kbps': (
|
|
1317
|
-
Math.random() * 1500 +
|
|
1318
|
-
800
|
|
1319
|
-
).toFixed(3),
|
|
1045
|
+
'x-ig-bandwidth-speed-kbps': (Math.random() * 1500 + 800).toFixed(3),
|
|
1320
1046
|
'x-ig-bandwidth-totalbytes-b': '0',
|
|
1321
1047
|
'x-ig-bandwidth-totaltime-ms': '0',
|
|
1322
1048
|
'x-ig-client-endpoint':
|
|
1323
1049
|
'com.bloks.www.caa.login.aymh_single_profile_screen_entry',
|
|
1324
1050
|
'x-ig-capabilities': '3brTv10=',
|
|
1325
|
-
'x-ig-connection-type': '
|
|
1051
|
+
'x-ig-connection-type': 'MOBILE(UNKNOWN)',
|
|
1326
1052
|
'x-ig-device-id': qeDeviceId,
|
|
1327
1053
|
'x-ig-device-locale': lang,
|
|
1328
1054
|
'x-ig-family-device-id': familyDeviceId,
|
|
@@ -1330,29 +1056,20 @@ class AccountRepository extends Repository {
|
|
|
1330
1056
|
'x-ig-nav-chain':
|
|
1331
1057
|
'LockoutFragment:dogfooding_lockout:1:cold_start:...,com.bloks.www.caa.login.aymh_single_profile_screen_entry:com.bloks.www.caa.login.aymh_single_profile_screen_entry:14:button:0:::0',
|
|
1332
1058
|
'x-ig-timezone-offset': String(
|
|
1333
|
-
typeof this.client.state.timezoneOffset ===
|
|
1334
|
-
'number'
|
|
1059
|
+
typeof this.client.state.timezoneOffset === 'number'
|
|
1335
1060
|
? this.client.state.timezoneOffset
|
|
1336
1061
|
: 7200
|
|
1337
1062
|
),
|
|
1338
1063
|
'x-ig-www-claim': this.client.state.igWWWClaim || '0',
|
|
1339
1064
|
'x-mid': machineId,
|
|
1340
|
-
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
|
|
1341
|
-
Math.random() * 1000
|
|
1342
|
-
)
|
|
1343
|
-
.toString()
|
|
1344
|
-
.padStart(3, '0')}`,
|
|
1065
|
+
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(Math.random() * 1000).toString().padStart(3, '0')}`,
|
|
1345
1066
|
'x-pigeon-session-id':
|
|
1346
1067
|
this.client.state.pigeonSessionId ||
|
|
1347
|
-
`UFS-${crypto.randomBytes(16).toString(
|
|
1348
|
-
'hex'
|
|
1349
|
-
)}-1`,
|
|
1068
|
+
`UFS-${crypto.randomBytes(16).toString('hex')}-1`,
|
|
1350
1069
|
'x-tigon-is-retry': 'False',
|
|
1351
|
-
|
|
1352
|
-
'accept-encoding': 'gzip, deflate, br',
|
|
1070
|
+
'accept-encoding': 'gzip, deflate',
|
|
1353
1071
|
'user-agent': userAgent,
|
|
1354
|
-
'x-fb-conn-uuid-client':
|
|
1355
|
-
crypto.randomBytes(16).toString('hex'),
|
|
1072
|
+
'x-fb-conn-uuid-client': crypto.randomBytes(16).toString('hex'),
|
|
1356
1073
|
'x-fb-http-engine': 'MNS/TCP',
|
|
1357
1074
|
'x-fb-rmd': 'state=URL_ELIGIBLE',
|
|
1358
1075
|
};
|
|
@@ -1368,44 +1085,18 @@ class AccountRepository extends Repository {
|
|
|
1368
1085
|
headers: bloksHeaders,
|
|
1369
1086
|
});
|
|
1370
1087
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
'authinfo_instagram'
|
|
1378
|
-
);
|
|
1379
|
-
const debugFile = path.join(
|
|
1380
|
-
debugDir,
|
|
1381
|
-
'login-debug.json'
|
|
1382
|
-
);
|
|
1383
|
-
try {
|
|
1384
|
-
fs.mkdirSync(debugDir, { recursive: true });
|
|
1385
|
-
} catch (e) {}
|
|
1386
|
-
|
|
1387
|
-
const debugPayload = {
|
|
1388
|
-
at: new Date().toISOString(),
|
|
1389
|
-
statusCode:
|
|
1390
|
-
response.statusCode || response.status || null,
|
|
1391
|
-
headers: response.headers || null,
|
|
1392
|
-
body: response.body || null,
|
|
1393
|
-
};
|
|
1394
|
-
fs.writeFileSync(
|
|
1395
|
-
debugFile,
|
|
1396
|
-
JSON.stringify(debugPayload, null, 2),
|
|
1397
|
-
'utf8'
|
|
1398
|
-
);
|
|
1399
|
-
} catch (e) {}
|
|
1088
|
+
debugWrite('login-debug.json', {
|
|
1089
|
+
at: new Date().toISOString(),
|
|
1090
|
+
statusCode: response.statusCode || response.status || null,
|
|
1091
|
+
headers: response.headers || null,
|
|
1092
|
+
body: response.body || null,
|
|
1093
|
+
});
|
|
1400
1094
|
|
|
1401
1095
|
const body = response.body;
|
|
1402
|
-
|
|
1403
1096
|
this._extractAndSaveAuthorization(response);
|
|
1404
1097
|
|
|
1405
1098
|
if (body && body.two_factor_required) {
|
|
1406
|
-
const err = new Error(
|
|
1407
|
-
'Two factor authentication required'
|
|
1408
|
-
);
|
|
1099
|
+
const err = new Error('Two factor authentication required');
|
|
1409
1100
|
err.name = 'IgLoginTwoFactorRequiredError';
|
|
1410
1101
|
err.twoFactorInfo = body.two_factor_info;
|
|
1411
1102
|
throw err;
|
|
@@ -1429,42 +1120,26 @@ class AccountRepository extends Repository {
|
|
|
1429
1120
|
|
|
1430
1121
|
if (body && body.layout) {
|
|
1431
1122
|
const layoutStr =
|
|
1432
|
-
typeof body.layout === 'string'
|
|
1433
|
-
? body.layout
|
|
1434
|
-
: JSON.stringify(body.layout);
|
|
1123
|
+
typeof body.layout === 'string' ? body.layout : JSON.stringify(body.layout);
|
|
1435
1124
|
|
|
1436
|
-
if (
|
|
1437
|
-
layoutStr.includes('two_factor_required') ||
|
|
1438
|
-
layoutStr.includes('"two_factor_info"')
|
|
1439
|
-
) {
|
|
1125
|
+
if (layoutStr.includes('two_factor_required') || layoutStr.includes('"two_factor_info"')) {
|
|
1440
1126
|
let twoFactorInfo = null;
|
|
1441
1127
|
try {
|
|
1442
|
-
const match = layoutStr.match(
|
|
1443
|
-
|
|
1444
|
-
);
|
|
1445
|
-
if (match)
|
|
1446
|
-
twoFactorInfo = JSON.parse(match[1]);
|
|
1128
|
+
const match = layoutStr.match(/"two_factor_info"\s*:\s*(\{[^}]+\})/);
|
|
1129
|
+
if (match) twoFactorInfo = JSON.parse(match[1]);
|
|
1447
1130
|
} catch (e) {}
|
|
1448
|
-
const err = new Error(
|
|
1449
|
-
'Two factor authentication required'
|
|
1450
|
-
);
|
|
1131
|
+
const err = new Error('Two factor authentication required');
|
|
1451
1132
|
err.name = 'IgLoginTwoFactorRequiredError';
|
|
1452
1133
|
err.twoFactorInfo = twoFactorInfo;
|
|
1453
1134
|
throw err;
|
|
1454
1135
|
}
|
|
1455
1136
|
|
|
1456
|
-
if (
|
|
1457
|
-
layoutStr.includes('bad_password') ||
|
|
1458
|
-
layoutStr.includes('incorrect_password')
|
|
1459
|
-
) {
|
|
1137
|
+
if (layoutStr.includes('bad_password') || layoutStr.includes('incorrect_password')) {
|
|
1460
1138
|
const err = new Error('Bad password');
|
|
1461
1139
|
err.name = 'IgLoginBadPasswordError';
|
|
1462
1140
|
throw err;
|
|
1463
1141
|
}
|
|
1464
|
-
if (
|
|
1465
|
-
layoutStr.includes('invalid_user') ||
|
|
1466
|
-
layoutStr.includes('user_not_found')
|
|
1467
|
-
) {
|
|
1142
|
+
if (layoutStr.includes('invalid_user') || layoutStr.includes('user_not_found')) {
|
|
1468
1143
|
const err = new Error('Invalid user');
|
|
1469
1144
|
err.name = 'IgLoginInvalidUserError';
|
|
1470
1145
|
throw err;
|
|
@@ -1476,14 +1151,10 @@ class AccountRepository extends Repository {
|
|
|
1476
1151
|
throw err;
|
|
1477
1152
|
}
|
|
1478
1153
|
|
|
1479
|
-
const tokenFromLayout =
|
|
1480
|
-
this._findBearerInString(layoutStr);
|
|
1154
|
+
const tokenFromLayout = this._findBearerInString(layoutStr);
|
|
1481
1155
|
if (tokenFromLayout) {
|
|
1482
|
-
this.client.state.authorization =
|
|
1483
|
-
|
|
1484
|
-
try {
|
|
1485
|
-
this.client.state.updateAuthorization();
|
|
1486
|
-
} catch (e) {}
|
|
1156
|
+
this.client.state.authorization = tokenFromLayout;
|
|
1157
|
+
try { this.client.state.updateAuthorization(); } catch (e) {}
|
|
1487
1158
|
}
|
|
1488
1159
|
|
|
1489
1160
|
const pkPatterns = [
|
|
@@ -1499,8 +1170,7 @@ class AccountRepository extends Repository {
|
|
|
1499
1170
|
const pkMatch = layoutStr.match(pattern);
|
|
1500
1171
|
if (pkMatch) {
|
|
1501
1172
|
extractedPk = pkMatch[1];
|
|
1502
|
-
this.client.state.cookieUserId =
|
|
1503
|
-
extractedPk;
|
|
1173
|
+
this.client.state.cookieUserId = extractedPk;
|
|
1504
1174
|
this.client.state._userId = extractedPk;
|
|
1505
1175
|
break;
|
|
1506
1176
|
}
|
|
@@ -1513,89 +1183,54 @@ class AccountRepository extends Repository {
|
|
|
1513
1183
|
/x-ig-set-www-claim[\\"\s:]+([a-f0-9]+)/i,
|
|
1514
1184
|
];
|
|
1515
1185
|
for (const pattern of wwwClaimPatterns) {
|
|
1516
|
-
const
|
|
1517
|
-
if (
|
|
1518
|
-
this.client.state.igWWWClaim =
|
|
1519
|
-
wwwClaimMatch[1];
|
|
1520
|
-
break;
|
|
1521
|
-
}
|
|
1186
|
+
const m = layoutStr.match(pattern);
|
|
1187
|
+
if (m) { this.client.state.igWWWClaim = m[1]; break; }
|
|
1522
1188
|
}
|
|
1523
1189
|
|
|
1524
1190
|
const encKeyIdMatch2 =
|
|
1525
|
-
layoutStr.match(
|
|
1526
|
-
|
|
1527
|
-
) ||
|
|
1528
|
-
layoutStr.match(
|
|
1529
|
-
/password.encryption.key.id[\\"\s:]+(\d+)/i
|
|
1530
|
-
);
|
|
1191
|
+
layoutStr.match(/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i) ||
|
|
1192
|
+
layoutStr.match(/password.encryption.key.id[\\"\s:]+(\d+)/i);
|
|
1531
1193
|
if (encKeyIdMatch2) {
|
|
1532
|
-
this.client.state.passwordEncryptionKeyId =
|
|
1533
|
-
parseInt(encKeyIdMatch2[1]);
|
|
1194
|
+
this.client.state.passwordEncryptionKeyId = parseInt(encKeyIdMatch2[1]);
|
|
1534
1195
|
}
|
|
1535
1196
|
|
|
1536
1197
|
const encPubKeyMatch2 =
|
|
1537
|
-
layoutStr.match(
|
|
1538
|
-
|
|
1539
|
-
) ||
|
|
1540
|
-
layoutStr.match(
|
|
1541
|
-
/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
1542
|
-
);
|
|
1198
|
+
layoutStr.match(/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i) ||
|
|
1199
|
+
layoutStr.match(/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i);
|
|
1543
1200
|
if (encPubKeyMatch2) {
|
|
1544
|
-
this.client.state.passwordEncryptionPubKey =
|
|
1545
|
-
encPubKeyMatch2[1];
|
|
1201
|
+
this.client.state.passwordEncryptionPubKey = encPubKeyMatch2[1];
|
|
1546
1202
|
}
|
|
1547
1203
|
|
|
1548
1204
|
const midMatch2 =
|
|
1549
|
-
layoutStr.match(
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
layoutStr.match(
|
|
1553
|
-
/\\"x-mid\\"[\\"\s:]+([A-Za-z0-9+\/=_-]+)/i
|
|
1554
|
-
);
|
|
1555
|
-
if (midMatch2) {
|
|
1556
|
-
this.client.state.mid = midMatch2[1];
|
|
1557
|
-
}
|
|
1205
|
+
layoutStr.match(/ig-set-x-mid[\\"\s:]+([A-Za-z0-9+\/=_-]+)/i) ||
|
|
1206
|
+
layoutStr.match(/\\"x-mid\\"[\\"\s:]+([A-Za-z0-9+\/=_-]+)/i);
|
|
1207
|
+
if (midMatch2) this.client.state.mid = midMatch2[1];
|
|
1558
1208
|
|
|
1559
1209
|
const loginResponsePatterns = [
|
|
1560
|
-
/\\"logged_in_user\\":\{[^}]*\\"pk\\":(
|
|
1210
|
+
/\\"logged_in_user\\":\{[^}]*\\"pk\\":(\\d+)[^}]*\\"username\\":\\"([^"\\]+)\\"/,
|
|
1561
1211
|
/"logged_in_user":\{[^}]*"pk":(\d+)[^}]*"username":"([^"]+)"/,
|
|
1562
1212
|
/\\"logged_in_user\\".*?\\"pk\\":\s*(\d+).*?\\"username\\":\s*\\"([^"\\]+)\\"/s,
|
|
1563
1213
|
];
|
|
1564
1214
|
for (const pattern of loginResponsePatterns) {
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
this.client.state.
|
|
1569
|
-
|
|
1570
|
-
this.client.state._userId =
|
|
1571
|
-
loginResponseMatch[1];
|
|
1572
|
-
return {
|
|
1573
|
-
pk: parseInt(loginResponseMatch[1]),
|
|
1574
|
-
pk_id: loginResponseMatch[1],
|
|
1575
|
-
username: loginResponseMatch[2],
|
|
1576
|
-
};
|
|
1215
|
+
const m = layoutStr.match(pattern);
|
|
1216
|
+
if (m) {
|
|
1217
|
+
this.client.state.cookieUserId = m[1];
|
|
1218
|
+
this.client.state._userId = m[1];
|
|
1219
|
+
return { pk: parseInt(m[1]), pk_id: m[1], username: m[2] };
|
|
1577
1220
|
}
|
|
1578
1221
|
}
|
|
1579
1222
|
|
|
1580
|
-
if (
|
|
1581
|
-
extractedPk &&
|
|
1582
|
-
this.client.state.authorization
|
|
1583
|
-
) {
|
|
1223
|
+
if (extractedPk && this.client.state.authorization) {
|
|
1584
1224
|
try {
|
|
1585
1225
|
const userInfo = await this.currentUser();
|
|
1586
1226
|
const user = userInfo.user || userInfo;
|
|
1587
1227
|
if (user && user.pk) {
|
|
1588
|
-
this.client.state.cookieUserId =
|
|
1589
|
-
|
|
1590
|
-
this.client.state._userId =
|
|
1591
|
-
String(user.pk);
|
|
1228
|
+
this.client.state.cookieUserId = String(user.pk);
|
|
1229
|
+
this.client.state._userId = String(user.pk);
|
|
1592
1230
|
}
|
|
1593
1231
|
return user;
|
|
1594
1232
|
} catch (e) {
|
|
1595
|
-
return {
|
|
1596
|
-
pk: parseInt(extractedPk),
|
|
1597
|
-
pk_id: extractedPk,
|
|
1598
|
-
};
|
|
1233
|
+
return { pk: parseInt(extractedPk), pk_id: extractedPk };
|
|
1599
1234
|
}
|
|
1600
1235
|
}
|
|
1601
1236
|
}
|
|
@@ -1615,8 +1250,7 @@ class AccountRepository extends Repository {
|
|
|
1615
1250
|
const userInfo = await this.currentUser();
|
|
1616
1251
|
const user = userInfo.user || userInfo;
|
|
1617
1252
|
if (user && user.pk) {
|
|
1618
|
-
this.client.state.cookieUserId =
|
|
1619
|
-
String(user.pk);
|
|
1253
|
+
this.client.state.cookieUserId = String(user.pk);
|
|
1620
1254
|
this.client.state._userId = String(user.pk);
|
|
1621
1255
|
}
|
|
1622
1256
|
return user;
|
|
@@ -1629,12 +1263,7 @@ class AccountRepository extends Repository {
|
|
|
1629
1263
|
});
|
|
1630
1264
|
}
|
|
1631
1265
|
|
|
1632
|
-
async twoFactorLogin(
|
|
1633
|
-
username,
|
|
1634
|
-
verificationCode,
|
|
1635
|
-
twoFactorIdentifier,
|
|
1636
|
-
verificationMethod = '1'
|
|
1637
|
-
) {
|
|
1266
|
+
async twoFactorLogin(username, verificationCode, twoFactorIdentifier, verificationMethod = '1') {
|
|
1638
1267
|
return this.requestWithRetry(async () => {
|
|
1639
1268
|
const response = await this.client.request.send({
|
|
1640
1269
|
method: 'POST',
|
|
@@ -1660,9 +1289,7 @@ class AccountRepository extends Repository {
|
|
|
1660
1289
|
const response = await this.client.request.send({
|
|
1661
1290
|
method: 'POST',
|
|
1662
1291
|
url: '/api/v1/accounts/logout/',
|
|
1663
|
-
form: this.client.request.sign({
|
|
1664
|
-
_uuid: this.client.state.uuid,
|
|
1665
|
-
}),
|
|
1292
|
+
form: this.client.request.sign({ _uuid: this.client.state.uuid }),
|
|
1666
1293
|
});
|
|
1667
1294
|
return response.body;
|
|
1668
1295
|
});
|
|
@@ -1683,14 +1310,7 @@ class AccountRepository extends Repository {
|
|
|
1683
1310
|
return this.currentUser();
|
|
1684
1311
|
}
|
|
1685
1312
|
|
|
1686
|
-
async editProfile({
|
|
1687
|
-
externalUrl,
|
|
1688
|
-
phoneNumber,
|
|
1689
|
-
username,
|
|
1690
|
-
fullName,
|
|
1691
|
-
biography,
|
|
1692
|
-
email,
|
|
1693
|
-
} = {}) {
|
|
1313
|
+
async editProfile({ externalUrl, phoneNumber, username, fullName, biography, email } = {}) {
|
|
1694
1314
|
return this.requestWithRetry(async () => {
|
|
1695
1315
|
const current = await this.currentUser();
|
|
1696
1316
|
const user = current.user || current;
|
|
@@ -1699,57 +1319,28 @@ class AccountRepository extends Repository {
|
|
|
1699
1319
|
url: '/api/v1/accounts/edit_profile/',
|
|
1700
1320
|
form: this.client.request.sign({
|
|
1701
1321
|
_uuid: this.client.state.uuid,
|
|
1702
|
-
external_url:
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
? phoneNumber
|
|
1709
|
-
: user.phone_number || '',
|
|
1710
|
-
username:
|
|
1711
|
-
username !== undefined
|
|
1712
|
-
? username
|
|
1713
|
-
: user.username,
|
|
1714
|
-
full_name:
|
|
1715
|
-
fullName !== undefined
|
|
1716
|
-
? fullName
|
|
1717
|
-
: user.full_name || '',
|
|
1718
|
-
biography:
|
|
1719
|
-
biography !== undefined
|
|
1720
|
-
? biography
|
|
1721
|
-
: user.biography || '',
|
|
1722
|
-
email:
|
|
1723
|
-
email !== undefined
|
|
1724
|
-
? email
|
|
1725
|
-
: user.email || '',
|
|
1322
|
+
external_url: externalUrl !== undefined ? externalUrl : user.external_url || '',
|
|
1323
|
+
phone_number: phoneNumber !== undefined ? phoneNumber : user.phone_number || '',
|
|
1324
|
+
username: username !== undefined ? username : user.username,
|
|
1325
|
+
full_name: fullName !== undefined ? fullName : user.full_name || '',
|
|
1326
|
+
biography: biography !== undefined ? biography : user.biography || '',
|
|
1327
|
+
email: email !== undefined ? email : user.email || '',
|
|
1726
1328
|
}),
|
|
1727
1329
|
});
|
|
1728
1330
|
return response.body;
|
|
1729
1331
|
});
|
|
1730
1332
|
}
|
|
1731
1333
|
|
|
1732
|
-
async setBiography(biography) {
|
|
1733
|
-
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
async setExternalUrl(url) {
|
|
1737
|
-
return this.editProfile({ externalUrl: url });
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
async removeBioLinks() {
|
|
1741
|
-
return this.editProfile({ externalUrl: '' });
|
|
1742
|
-
}
|
|
1334
|
+
async setBiography(biography) { return this.editProfile({ biography }); }
|
|
1335
|
+
async setExternalUrl(url) { return this.editProfile({ externalUrl: url }); }
|
|
1336
|
+
async removeBioLinks() { return this.editProfile({ externalUrl: '' }); }
|
|
1743
1337
|
|
|
1744
1338
|
async setGender(gender) {
|
|
1745
1339
|
return this.requestWithRetry(async () => {
|
|
1746
1340
|
const response = await this.client.request.send({
|
|
1747
1341
|
method: 'POST',
|
|
1748
1342
|
url: '/api/v1/accounts/set_gender/',
|
|
1749
|
-
form: this.client.request.sign({
|
|
1750
|
-
_uuid: this.client.state.uuid,
|
|
1751
|
-
gender,
|
|
1752
|
-
}),
|
|
1343
|
+
form: this.client.request.sign({ _uuid: this.client.state.uuid, gender }),
|
|
1753
1344
|
});
|
|
1754
1345
|
return response.body;
|
|
1755
1346
|
});
|
|
@@ -1760,9 +1351,7 @@ class AccountRepository extends Repository {
|
|
|
1760
1351
|
const response = await this.client.request.send({
|
|
1761
1352
|
method: 'POST',
|
|
1762
1353
|
url: '/api/v1/accounts/set_private/',
|
|
1763
|
-
form: this.client.request.sign({
|
|
1764
|
-
_uuid: this.client.state.uuid,
|
|
1765
|
-
}),
|
|
1354
|
+
form: this.client.request.sign({ _uuid: this.client.state.uuid }),
|
|
1766
1355
|
});
|
|
1767
1356
|
return response.body;
|
|
1768
1357
|
});
|
|
@@ -1773,9 +1362,7 @@ class AccountRepository extends Repository {
|
|
|
1773
1362
|
const response = await this.client.request.send({
|
|
1774
1363
|
method: 'POST',
|
|
1775
1364
|
url: '/api/v1/accounts/set_public/',
|
|
1776
|
-
form: this.client.request.sign({
|
|
1777
|
-
_uuid: this.client.state.uuid,
|
|
1778
|
-
}),
|
|
1365
|
+
form: this.client.request.sign({ _uuid: this.client.state.uuid }),
|
|
1779
1366
|
});
|
|
1780
1367
|
return response.body;
|
|
1781
1368
|
});
|
|
@@ -1804,10 +1391,7 @@ class AccountRepository extends Repository {
|
|
|
1804
1391
|
const response = await this.client.request.send({
|
|
1805
1392
|
method: 'POST',
|
|
1806
1393
|
url: '/api/v1/accounts/send_confirm_email/',
|
|
1807
|
-
form: this.client.request.sign({
|
|
1808
|
-
_uuid: this.client.state.uuid,
|
|
1809
|
-
send_source: 'edit_profile',
|
|
1810
|
-
}),
|
|
1394
|
+
form: this.client.request.sign({ _uuid: this.client.state.uuid, send_source: 'edit_profile' }),
|
|
1811
1395
|
});
|
|
1812
1396
|
return response.body;
|
|
1813
1397
|
});
|
|
@@ -1818,10 +1402,7 @@ class AccountRepository extends Repository {
|
|
|
1818
1402
|
const response = await this.client.request.send({
|
|
1819
1403
|
method: 'POST',
|
|
1820
1404
|
url: '/api/v1/accounts/send_confirm_phone_number/',
|
|
1821
|
-
form: this.client.request.sign({
|
|
1822
|
-
_uuid: this.client.state.uuid,
|
|
1823
|
-
phone_number: phoneNumber,
|
|
1824
|
-
}),
|
|
1405
|
+
form: this.client.request.sign({ _uuid: this.client.state.uuid, phone_number: phoneNumber }),
|
|
1825
1406
|
});
|
|
1826
1407
|
return response.body;
|
|
1827
1408
|
});
|
|
@@ -1847,9 +1428,7 @@ class AccountRepository extends Repository {
|
|
|
1847
1428
|
const response = await this.client.request.send({
|
|
1848
1429
|
method: 'POST',
|
|
1849
1430
|
url: '/api/v1/accounts/remove_profile_picture/',
|
|
1850
|
-
form: this.client.request.sign({
|
|
1851
|
-
_uuid: this.client.state.uuid,
|
|
1852
|
-
}),
|
|
1431
|
+
form: this.client.request.sign({ _uuid: this.client.state.uuid }),
|
|
1853
1432
|
});
|
|
1854
1433
|
return response.body;
|
|
1855
1434
|
});
|
|
@@ -1873,9 +1452,7 @@ class AccountRepository extends Repository {
|
|
|
1873
1452
|
form: this.client.request.sign({
|
|
1874
1453
|
id: this.client.state.uuid,
|
|
1875
1454
|
server_config_retrieval: '1',
|
|
1876
|
-
experiments:
|
|
1877
|
-
this.client.state.constants
|
|
1878
|
-
.LOGIN_EXPERIMENTS,
|
|
1455
|
+
experiments: this.client.state.constants.LOGIN_EXPERIMENTS,
|
|
1879
1456
|
}),
|
|
1880
1457
|
});
|
|
1881
1458
|
return response.body;
|
|
@@ -1884,12 +1461,7 @@ class AccountRepository extends Repository {
|
|
|
1884
1461
|
|
|
1885
1462
|
async syncPostLoginExperiments() {
|
|
1886
1463
|
let userId;
|
|
1887
|
-
try {
|
|
1888
|
-
userId = this.client.state.cookieUserId;
|
|
1889
|
-
} catch {
|
|
1890
|
-
userId = '0';
|
|
1891
|
-
}
|
|
1892
|
-
|
|
1464
|
+
try { userId = this.client.state.cookieUserId; } catch { userId = '0'; }
|
|
1893
1465
|
return this.requestWithRetry(async () => {
|
|
1894
1466
|
const response = await this.client.request.send({
|
|
1895
1467
|
method: 'POST',
|
|
@@ -1899,8 +1471,7 @@ class AccountRepository extends Repository {
|
|
|
1899
1471
|
_uid: userId,
|
|
1900
1472
|
_uuid: this.client.state.uuid,
|
|
1901
1473
|
server_config_retrieval: '1',
|
|
1902
|
-
experiments:
|
|
1903
|
-
this.client.state.constants.EXPERIMENTS,
|
|
1474
|
+
experiments: this.client.state.constants.EXPERIMENTS,
|
|
1904
1475
|
}),
|
|
1905
1476
|
});
|
|
1906
1477
|
return response.body;
|
|
@@ -1908,17 +1479,13 @@ class AccountRepository extends Repository {
|
|
|
1908
1479
|
}
|
|
1909
1480
|
|
|
1910
1481
|
async syncLauncher(preLogin = true) {
|
|
1911
|
-
const data = {
|
|
1912
|
-
id: this.client.state.uuid,
|
|
1913
|
-
server_config_retrieval: '1',
|
|
1914
|
-
};
|
|
1482
|
+
const data = { id: this.client.state.uuid, server_config_retrieval: '1' };
|
|
1915
1483
|
if (!preLogin) {
|
|
1916
1484
|
try {
|
|
1917
1485
|
data._uid = this.client.state.cookieUserId;
|
|
1918
1486
|
data._uuid = this.client.state.uuid;
|
|
1919
1487
|
} catch {}
|
|
1920
1488
|
}
|
|
1921
|
-
|
|
1922
1489
|
return this.requestWithRetry(async () => {
|
|
1923
1490
|
const response = await this.client.request.send({
|
|
1924
1491
|
method: 'POST',
|
|
@@ -1934,10 +1501,7 @@ class AccountRepository extends Repository {
|
|
|
1934
1501
|
const response = await this.client.request.send({
|
|
1935
1502
|
method: 'POST',
|
|
1936
1503
|
url: '/api/v1/devices/sync/',
|
|
1937
|
-
form: this.client.request.sign({
|
|
1938
|
-
id: this.client.state.uuid,
|
|
1939
|
-
server_config_retrieval: '1',
|
|
1940
|
-
}),
|
|
1504
|
+
form: this.client.request.sign({ id: this.client.state.uuid, server_config_retrieval: '1' }),
|
|
1941
1505
|
});
|
|
1942
1506
|
return response.body;
|
|
1943
1507
|
});
|
|
@@ -1964,10 +1528,7 @@ class AccountRepository extends Repository {
|
|
|
1964
1528
|
const response = await this.client.request.send({
|
|
1965
1529
|
method: 'POST',
|
|
1966
1530
|
url: '/api/v1/accounts/contact_point_prefill/',
|
|
1967
|
-
form: this.client.request.sign({
|
|
1968
|
-
phone_id: this.client.state.phoneId,
|
|
1969
|
-
usage,
|
|
1970
|
-
}),
|
|
1531
|
+
form: this.client.request.sign({ phone_id: this.client.state.phoneId, usage }),
|
|
1971
1532
|
});
|
|
1972
1533
|
return response.body;
|
|
1973
1534
|
});
|
|
@@ -1995,10 +1556,7 @@ class AccountRepository extends Repository {
|
|
|
1995
1556
|
const response = await this.client.request.send({
|
|
1996
1557
|
method: 'GET',
|
|
1997
1558
|
url: '/api/v1/consent/get_signup_config/',
|
|
1998
|
-
qs: {
|
|
1999
|
-
guid: this.client.state.uuid,
|
|
2000
|
-
main_account_selected: false,
|
|
2001
|
-
},
|
|
1559
|
+
qs: { guid: this.client.state.uuid, main_account_selected: false },
|
|
2002
1560
|
});
|
|
2003
1561
|
return response.body;
|
|
2004
1562
|
});
|
|
@@ -2036,91 +1594,70 @@ class AccountRepository extends Repository {
|
|
|
2036
1594
|
});
|
|
2037
1595
|
}
|
|
2038
1596
|
|
|
1597
|
+
/**
|
|
1598
|
+
* FIX MAJOR — generateAttestParams: each certificate in the chain
|
|
1599
|
+
* now has its own EC key pair, correctly simulating the
|
|
1600
|
+
* Android Keystore hierarchy (leaf → intermediate1 → intermediate2 → root).
|
|
1601
|
+
*/
|
|
2039
1602
|
static generateAttestParams(state) {
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
1603
|
+
// Key for leaf (the actually attested key)
|
|
1604
|
+
const leafKey = crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
|
|
1605
|
+
|
|
1606
|
+
const challengeNonce = crypto.randomBytes(24).toString('base64url');
|
|
1607
|
+
const signedData = crypto.sign(null, Buffer.from(challengeNonce), leafKey.privateKey);
|
|
1608
|
+
const signedNonce = signedData.toString('base64');
|
|
1609
|
+
const publicKeyDer = leafKey.publicKey.export({ type: 'spki', format: 'der' });
|
|
1610
|
+
const keyHash = crypto.createHash('sha256').update(publicKeyDer).digest('hex');
|
|
1611
|
+
|
|
1612
|
+
// Separate keys for each level in the chain
|
|
1613
|
+
const int1Key = crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
|
|
1614
|
+
const int2Key = crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
|
|
1615
|
+
const rootKey = crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
|
|
1616
|
+
|
|
1617
|
+
const leafCertPem = AccountRepository._generateSignedCert(
|
|
1618
|
+
leafKey.privateKey, leafKey.publicKey, 'Android Keystore Key',
|
|
1619
|
+
int1Key.privateKey // signed by intermediate 1
|
|
2048
1620
|
);
|
|
2049
|
-
const
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
1621
|
+
const intermediate1Pem = AccountRepository._generateSignedCert(
|
|
1622
|
+
int1Key.privateKey, int1Key.publicKey, 'Android Keystore Key Attestation',
|
|
1623
|
+
int2Key.privateKey // signed by intermediate 2
|
|
1624
|
+
);
|
|
1625
|
+
const intermediate2Pem = AccountRepository._generateSignedCert(
|
|
1626
|
+
int2Key.privateKey, int2Key.publicKey, 'Android Hardware Keystore',
|
|
1627
|
+
rootKey.privateKey // self-signed root
|
|
1628
|
+
);
|
|
1629
|
+
const rootCertPem = AccountRepository._generateSignedCert(
|
|
1630
|
+
rootKey.privateKey, rootKey.publicKey, 'Android Keystore Root',
|
|
1631
|
+
rootKey.privateKey // self-signed root
|
|
2053
1632
|
);
|
|
2054
|
-
const signedNonce = signedData.toString('base64');
|
|
2055
|
-
const publicKeyDer = publicKey.export({
|
|
2056
|
-
type: 'spki',
|
|
2057
|
-
format: 'der',
|
|
2058
|
-
});
|
|
2059
|
-
const keyHash = crypto
|
|
2060
|
-
.createHash('sha256')
|
|
2061
|
-
.update(publicKeyDer)
|
|
2062
|
-
.digest('hex');
|
|
2063
|
-
|
|
2064
|
-
const leafCertPem =
|
|
2065
|
-
AccountRepository._generateSelfSignedCert(
|
|
2066
|
-
privateKey,
|
|
2067
|
-
publicKey,
|
|
2068
|
-
'Android Keystore Key'
|
|
2069
|
-
);
|
|
2070
|
-
const intermediate1Pem =
|
|
2071
|
-
AccountRepository._generateSelfSignedCert(
|
|
2072
|
-
privateKey,
|
|
2073
|
-
publicKey,
|
|
2074
|
-
'Android Keystore Key Attestation'
|
|
2075
|
-
);
|
|
2076
|
-
const intermediate2Pem =
|
|
2077
|
-
AccountRepository._generateSelfSignedCert(
|
|
2078
|
-
privateKey,
|
|
2079
|
-
publicKey,
|
|
2080
|
-
'Android Hardware Keystore'
|
|
2081
|
-
);
|
|
2082
|
-
const rootCertPem =
|
|
2083
|
-
AccountRepository._generateSelfSignedCert(
|
|
2084
|
-
privateKey,
|
|
2085
|
-
publicKey,
|
|
2086
|
-
'Android Keystore Root'
|
|
2087
|
-
);
|
|
2088
1633
|
|
|
2089
|
-
const certificateChain = [
|
|
2090
|
-
leafCertPem,
|
|
2091
|
-
intermediate1Pem,
|
|
2092
|
-
intermediate2Pem,
|
|
2093
|
-
rootCertPem,
|
|
2094
|
-
].join('\n');
|
|
1634
|
+
const certificateChain = [leafCertPem, intermediate1Pem, intermediate2Pem, rootCertPem].join('\n');
|
|
2095
1635
|
|
|
2096
1636
|
return {
|
|
2097
|
-
attestation: [
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
},
|
|
2107
|
-
],
|
|
1637
|
+
attestation: [{
|
|
1638
|
+
version: 2,
|
|
1639
|
+
type: 'keystore',
|
|
1640
|
+
errors: [0],
|
|
1641
|
+
challenge_nonce: challengeNonce,
|
|
1642
|
+
signed_nonce: signedNonce,
|
|
1643
|
+
key_hash: keyHash,
|
|
1644
|
+
certificate_chain: certificateChain,
|
|
1645
|
+
}],
|
|
2108
1646
|
};
|
|
2109
1647
|
}
|
|
2110
1648
|
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
1649
|
+
/**
|
|
1650
|
+
* FIX: _generateSignedCert now accepts a separate signing key
|
|
1651
|
+
* (signerPrivateKey), enabling the correct certificate hierarchy.
|
|
1652
|
+
*/
|
|
1653
|
+
static _generateSignedCert(privateKey, publicKey, cn, signerPrivateKey) {
|
|
1654
|
+
if (!signerPrivateKey) signerPrivateKey = privateKey; // self-signed fallback
|
|
1655
|
+
|
|
1656
|
+
const publicKeyDer = publicKey.export({ type: 'spki', format: 'der' });
|
|
2116
1657
|
const serialNumber = crypto.randomBytes(8);
|
|
2117
1658
|
const now = new Date();
|
|
2118
|
-
const notBefore = new Date(
|
|
2119
|
-
|
|
2120
|
-
);
|
|
2121
|
-
const notAfter = new Date(
|
|
2122
|
-
now.getTime() + 10 * 365 * 24 * 60 * 60 * 1000
|
|
2123
|
-
);
|
|
1659
|
+
const notBefore = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000);
|
|
1660
|
+
const notAfter = new Date(now.getTime() + 10 * 365 * 24 * 60 * 60 * 1000);
|
|
2124
1661
|
const cnBytes = Buffer.from(cn, 'utf8');
|
|
2125
1662
|
const cnSeq = Buffer.concat([
|
|
2126
1663
|
Buffer.from([0x30, cnBytes.length + 13]),
|
|
@@ -2132,122 +1669,83 @@ class AccountRepository extends Repository {
|
|
|
2132
1669
|
]);
|
|
2133
1670
|
|
|
2134
1671
|
function encodeTime(date) {
|
|
2135
|
-
const str =
|
|
2136
|
-
|
|
2137
|
-
.toISOString()
|
|
2138
|
-
.replace(/[-:T]/g, '')
|
|
2139
|
-
.slice(2, 14) + 'Z';
|
|
2140
|
-
return Buffer.concat([
|
|
2141
|
-
Buffer.from([0x17, str.length]),
|
|
2142
|
-
Buffer.from(str),
|
|
2143
|
-
]);
|
|
1672
|
+
const str = date.toISOString().replace(/[-:T]/g, '').slice(2, 14) + 'Z';
|
|
1673
|
+
return Buffer.concat([Buffer.from([0x17, str.length]), Buffer.from(str)]);
|
|
2144
1674
|
}
|
|
2145
1675
|
|
|
2146
|
-
const validityBuf = Buffer.concat([
|
|
2147
|
-
|
|
2148
|
-
encodeTime(notAfter),
|
|
2149
|
-
]);
|
|
2150
|
-
const validity = Buffer.concat([
|
|
2151
|
-
Buffer.from([0x30, validityBuf.length]),
|
|
2152
|
-
validityBuf,
|
|
2153
|
-
]);
|
|
1676
|
+
const validityBuf = Buffer.concat([encodeTime(notBefore), encodeTime(notAfter)]);
|
|
1677
|
+
const validity = Buffer.concat([Buffer.from([0x30, validityBuf.length]), validityBuf]);
|
|
2154
1678
|
|
|
2155
1679
|
const tbs = Buffer.concat([
|
|
2156
1680
|
Buffer.from([0xa0, 0x03, 0x02, 0x01, 0x02]),
|
|
2157
1681
|
Buffer.from([0x02, serialNumber.length]),
|
|
2158
1682
|
serialNumber,
|
|
2159
|
-
Buffer.from([
|
|
2160
|
-
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
|
|
2161
|
-
0x04, 0x03, 0x02,
|
|
2162
|
-
]),
|
|
1683
|
+
Buffer.from([0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]),
|
|
2163
1684
|
cnSeq,
|
|
2164
1685
|
validity,
|
|
2165
1686
|
cnSeq,
|
|
2166
1687
|
publicKeyDer,
|
|
2167
1688
|
]);
|
|
2168
1689
|
|
|
2169
|
-
const tbsSeq = Buffer.concat([
|
|
2170
|
-
Buffer.from([0x30, 0x82]),
|
|
2171
|
-
Buffer.alloc(2),
|
|
2172
|
-
tbs,
|
|
2173
|
-
]);
|
|
1690
|
+
const tbsSeq = Buffer.concat([Buffer.from([0x30, 0x82]), Buffer.alloc(2), tbs]);
|
|
2174
1691
|
tbsSeq.writeUInt16BE(tbs.length, 2);
|
|
2175
1692
|
|
|
2176
|
-
|
|
1693
|
+
// FIX: signed with signerPrivateKey (can be an upper-level key)
|
|
1694
|
+
const signature = crypto.sign(null, tbsSeq, signerPrivateKey);
|
|
2177
1695
|
const sigBitString = Buffer.concat([
|
|
2178
1696
|
Buffer.from([0x03, signature.length + 1, 0x00]),
|
|
2179
1697
|
signature,
|
|
2180
1698
|
]);
|
|
2181
|
-
const algId = Buffer.from([
|
|
2182
|
-
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
|
|
2183
|
-
0x04, 0x03, 0x02,
|
|
2184
|
-
]);
|
|
1699
|
+
const algId = Buffer.from([0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]);
|
|
2185
1700
|
const certBody = Buffer.concat([tbsSeq, algId, sigBitString]);
|
|
2186
|
-
const cert = Buffer.concat([
|
|
2187
|
-
Buffer.from([0x30, 0x82]),
|
|
2188
|
-
Buffer.alloc(2),
|
|
2189
|
-
certBody,
|
|
2190
|
-
]);
|
|
1701
|
+
const cert = Buffer.concat([Buffer.from([0x30, 0x82]), Buffer.alloc(2), certBody]);
|
|
2191
1702
|
cert.writeUInt16BE(certBody.length, 2);
|
|
2192
1703
|
|
|
2193
1704
|
const b64 = cert.toString('base64');
|
|
2194
1705
|
const lines = b64.match(/.{1,76}/g) || [b64];
|
|
2195
|
-
return (
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
1706
|
+
return '-----BEGIN CERTIFICATE-----\n' + lines.join('\n') + '\n-----END CERTIFICATE-----';
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Kept for compatibility — internally calls _generateSignedCert
|
|
1710
|
+
static _generateSelfSignedCert(privateKey, publicKey, cn) {
|
|
1711
|
+
return AccountRepository._generateSignedCert(privateKey, publicKey, cn, privateKey);
|
|
2200
1712
|
}
|
|
2201
1713
|
|
|
2202
1714
|
static createJazoest(input) {
|
|
2203
1715
|
const buf = Buffer.from(input || '', 'ascii');
|
|
2204
1716
|
let sum = 0;
|
|
2205
|
-
for (let i = 0; i < buf.byteLength; i++)
|
|
2206
|
-
sum += buf.readUInt8(i);
|
|
2207
|
-
}
|
|
1717
|
+
for (let i = 0; i < buf.byteLength; i++) sum += buf.readUInt8(i);
|
|
2208
1718
|
return `2${sum}`;
|
|
2209
1719
|
}
|
|
2210
1720
|
|
|
2211
1721
|
encryptPassword(password) {
|
|
2212
1722
|
if (!this.client.state.passwordEncryptionPubKey) {
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
1723
|
+
// FIX: no longer returning password in plaintext — throws a controlled error
|
|
1724
|
+
throw new Error(
|
|
1725
|
+
'passwordEncryptionPubKey missing from state. ' +
|
|
1726
|
+
'Cannot encrypt password safely.'
|
|
1727
|
+
);
|
|
2217
1728
|
}
|
|
2218
1729
|
const randKey = crypto.randomBytes(32);
|
|
2219
1730
|
const iv = crypto.randomBytes(12);
|
|
2220
1731
|
const rsaEncrypted = crypto.publicEncrypt(
|
|
2221
1732
|
{
|
|
2222
|
-
key: Buffer.from(
|
|
2223
|
-
this.client.state.passwordEncryptionPubKey,
|
|
2224
|
-
'base64'
|
|
2225
|
-
).toString(),
|
|
1733
|
+
key: Buffer.from(this.client.state.passwordEncryptionPubKey, 'base64').toString(),
|
|
2226
1734
|
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
2227
1735
|
},
|
|
2228
1736
|
randKey
|
|
2229
1737
|
);
|
|
2230
|
-
const cipher = crypto.createCipheriv(
|
|
2231
|
-
'aes-256-gcm',
|
|
2232
|
-
randKey,
|
|
2233
|
-
iv
|
|
2234
|
-
);
|
|
1738
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', randKey, iv);
|
|
2235
1739
|
const time = Math.floor(Date.now() / 1000).toString();
|
|
2236
1740
|
cipher.setAAD(Buffer.from(time));
|
|
2237
|
-
const aesEncrypted = Buffer.concat([
|
|
2238
|
-
cipher.update(password, 'utf8'),
|
|
2239
|
-
cipher.final(),
|
|
2240
|
-
]);
|
|
1741
|
+
const aesEncrypted = Buffer.concat([cipher.update(password, 'utf8'), cipher.final()]);
|
|
2241
1742
|
const sizeBuffer = Buffer.alloc(2, 0);
|
|
2242
1743
|
sizeBuffer.writeInt16LE(rsaEncrypted.byteLength, 0);
|
|
2243
1744
|
const authTag = cipher.getAuthTag();
|
|
2244
1745
|
return {
|
|
2245
1746
|
time,
|
|
2246
1747
|
encrypted: Buffer.concat([
|
|
2247
|
-
Buffer.from([
|
|
2248
|
-
1,
|
|
2249
|
-
this.client.state.passwordEncryptionKeyId || 0,
|
|
2250
|
-
]),
|
|
1748
|
+
Buffer.from([1, this.client.state.passwordEncryptionKeyId || 0]),
|
|
2251
1749
|
iv,
|
|
2252
1750
|
sizeBuffer,
|
|
2253
1751
|
rsaEncrypted,
|
|
@@ -2258,16 +1756,10 @@ class AccountRepository extends Repository {
|
|
|
2258
1756
|
}
|
|
2259
1757
|
|
|
2260
1758
|
async passwordPublicKeys() {
|
|
2261
|
-
const response = await this.client.request.send({
|
|
2262
|
-
method: 'GET',
|
|
2263
|
-
url: '/api/v1/qe/sync/',
|
|
2264
|
-
});
|
|
1759
|
+
const response = await this.client.request.send({ method: 'GET', url: '/api/v1/qe/sync/' });
|
|
2265
1760
|
const headers = response.headers || {};
|
|
2266
|
-
const keyId = parseInt(
|
|
2267
|
-
|
|
2268
|
-
);
|
|
2269
|
-
const pubKey =
|
|
2270
|
-
headers['ig-set-password-encryption-pub-key'] || '';
|
|
1761
|
+
const keyId = parseInt(headers['ig-set-password-encryption-key-id'] || '0');
|
|
1762
|
+
const pubKey = headers['ig-set-password-encryption-pub-key'] || '';
|
|
2271
1763
|
return { keyId, pubKey };
|
|
2272
1764
|
}
|
|
2273
1765
|
|
|
@@ -2309,6 +1801,7 @@ class AccountRepository extends Repository {
|
|
|
2309
1801
|
});
|
|
2310
1802
|
}
|
|
2311
1803
|
|
|
1804
|
+
// CRITICAL FIX: 'preferen1gces' → 'preferences' (typo → ReferenceError fix)
|
|
2312
1805
|
async pushPreferences(preferences = 'default') {
|
|
2313
1806
|
return this.requestWithRetry(async () => {
|
|
2314
1807
|
const response = await this.client.request.send({
|
|
@@ -2321,7 +1814,7 @@ class AccountRepository extends Repository {
|
|
|
2321
1814
|
phone_id: this.client.state.phoneId,
|
|
2322
1815
|
device_token: '',
|
|
2323
1816
|
guid: this.client.state.uuid,
|
|
2324
|
-
users: preferen1gces
|
|
1817
|
+
users: preferences, // FIX: was 'preferen1gces'
|
|
2325
1818
|
}),
|
|
2326
1819
|
});
|
|
2327
1820
|
return response.body;
|