nodejs-insta-private-api-mqt 1.3.86 → 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.
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
const Repository = require('../core/repository');
|
|
2
2
|
const crypto = require('crypto');
|
|
3
|
+
|
|
3
4
|
class AccountRepository extends Repository {
|
|
4
5
|
constructor(client) {
|
|
5
6
|
super(client);
|
|
6
7
|
this.maxRetries = 3;
|
|
7
8
|
}
|
|
9
|
+
|
|
8
10
|
async requestWithRetry(requestFn, retries = 0) {
|
|
9
11
|
try {
|
|
10
12
|
const result = await requestFn();
|
|
11
13
|
return result;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
} catch (error) {
|
|
15
|
+
const shouldRetry =
|
|
16
|
+
(error.data?.error_type === 'server_error' ||
|
|
17
|
+
error.data?.error_type === 'rate_limited') &&
|
|
16
18
|
retries < this.maxRetries;
|
|
17
19
|
if (shouldRetry) {
|
|
18
20
|
const delay = 1000 * (retries + 1);
|
|
@@ -22,6 +24,7 @@ class AccountRepository extends Repository {
|
|
|
22
24
|
throw error;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
27
|
+
|
|
25
28
|
/**
|
|
26
29
|
* Helper: try to extract an IGT bearer token from various sources:
|
|
27
30
|
* - response.headers (IG-Set-Authorization or ig-set-authorization)
|
|
@@ -32,8 +35,8 @@ class AccountRepository extends Repository {
|
|
|
32
35
|
* When found, sets this.client.state.authorization and calls updateAuthorization().
|
|
33
36
|
*/
|
|
34
37
|
_extractAndSaveAuthorization(response) {
|
|
35
|
-
if (!response)
|
|
36
|
-
|
|
38
|
+
if (!response) return null;
|
|
39
|
+
|
|
37
40
|
// Normalize headers (case-insensitive)
|
|
38
41
|
const headers = {};
|
|
39
42
|
if (response.headers && typeof response.headers === 'object') {
|
|
@@ -41,37 +44,37 @@ class AccountRepository extends Repository {
|
|
|
41
44
|
headers[k.toLowerCase()] = response.headers[k];
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
|
|
47
|
+
|
|
48
|
+
// 1) Try straight headers for IG-Set-Authorization
|
|
45
49
|
for (const key of Object.keys(headers)) {
|
|
46
50
|
const val = headers[key];
|
|
47
|
-
if (!val)
|
|
48
|
-
|
|
51
|
+
if (!val) continue;
|
|
52
|
+
|
|
49
53
|
if (key.includes('ig-set-authorization')) {
|
|
50
54
|
const token = this._normalizeTokenString(val);
|
|
51
55
|
if (token) {
|
|
52
56
|
this.client.state.authorization = token;
|
|
53
57
|
try {
|
|
54
58
|
this.client.state.updateAuthorization();
|
|
55
|
-
}
|
|
56
|
-
catch (e) { }
|
|
59
|
+
} catch (e) {}
|
|
57
60
|
return token;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
|
-
// Some servers include the header value inside a JSON-like string in other headers
|
|
61
63
|
if (typeof val === 'string' && val.includes('Bearer IGT:2:')) {
|
|
62
64
|
const token = this._findBearerInString(val);
|
|
63
65
|
if (token) {
|
|
64
66
|
this.client.state.authorization = token;
|
|
65
67
|
try {
|
|
66
68
|
this.client.state.updateAuthorization();
|
|
67
|
-
}
|
|
68
|
-
catch (e) { }
|
|
69
|
+
} catch (e) {}
|
|
69
70
|
return token;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
}
|
|
74
|
+
|
|
73
75
|
// Extract IG-U headers (user id, region, claim) when present
|
|
74
|
-
const dsUserIdHeader =
|
|
76
|
+
const dsUserIdHeader =
|
|
77
|
+
headers['ig-set-ig-u-ds-user-id'] ||
|
|
75
78
|
headers['ig-u-ds-user-id'] ||
|
|
76
79
|
headers['x-ig-set-ig-u-ds-user-id'];
|
|
77
80
|
if (dsUserIdHeader) {
|
|
@@ -82,7 +85,9 @@ class AccountRepository extends Repository {
|
|
|
82
85
|
this.client.state._userId = uid;
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
|
-
|
|
88
|
+
|
|
89
|
+
const rurHeader =
|
|
90
|
+
headers['ig-set-ig-u-rur'] ||
|
|
86
91
|
headers['ig-u-rur'] ||
|
|
87
92
|
headers['x-ig-set-ig-u-rur'];
|
|
88
93
|
if (rurHeader) {
|
|
@@ -91,7 +96,9 @@ class AccountRepository extends Repository {
|
|
|
91
96
|
this.client.state.igURur = rur;
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
|
-
|
|
99
|
+
|
|
100
|
+
const wwwClaimHeader =
|
|
101
|
+
headers['x-ig-set-www-claim'] ||
|
|
95
102
|
headers['ig-set-www-claim'] ||
|
|
96
103
|
headers['ig-u-www-claim'];
|
|
97
104
|
if (wwwClaimHeader) {
|
|
@@ -100,7 +107,9 @@ class AccountRepository extends Repository {
|
|
|
100
107
|
this.client.state.igWWWClaim = claim;
|
|
101
108
|
}
|
|
102
109
|
}
|
|
103
|
-
|
|
110
|
+
|
|
111
|
+
const midHeader =
|
|
112
|
+
headers['ig-set-x-mid'] ||
|
|
104
113
|
headers['x-mid'] ||
|
|
105
114
|
headers['ig-set-mid'];
|
|
106
115
|
if (midHeader) {
|
|
@@ -109,83 +118,99 @@ class AccountRepository extends Repository {
|
|
|
109
118
|
this.client.state.mid = mid;
|
|
110
119
|
}
|
|
111
120
|
}
|
|
112
|
-
|
|
121
|
+
|
|
122
|
+
// 2) Check response.body for common fields / embedded layout/login_response
|
|
113
123
|
const body = response.body;
|
|
114
124
|
if (body) {
|
|
115
|
-
// If body has headers string (some responses embed headers JSON as a string)
|
|
116
125
|
if (body.headers && typeof body.headers === 'string') {
|
|
117
126
|
const token = this._findBearerInString(body.headers);
|
|
118
127
|
if (token) {
|
|
119
128
|
this.client.state.authorization = token;
|
|
120
129
|
try {
|
|
121
130
|
this.client.state.updateAuthorization();
|
|
122
|
-
}
|
|
123
|
-
catch (e) { }
|
|
131
|
+
} catch (e) {}
|
|
124
132
|
return token;
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
|
-
|
|
135
|
+
|
|
128
136
|
try {
|
|
129
137
|
let layoutStr = null;
|
|
130
138
|
if (body.layout) {
|
|
131
|
-
layoutStr = typeof body.layout === 'string'
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
layoutStr = typeof body.layout === 'string'
|
|
140
|
+
? body.layout
|
|
141
|
+
: JSON.stringify(body.layout);
|
|
142
|
+
} else if (body?.layout?.bloks_payload) {
|
|
134
143
|
layoutStr = JSON.stringify(body.layout.bloks_payload);
|
|
135
|
-
}
|
|
136
|
-
else if (body?.bloks_payload) {
|
|
144
|
+
} else if (body?.bloks_payload) {
|
|
137
145
|
layoutStr = JSON.stringify(body.bloks_payload);
|
|
138
|
-
}
|
|
139
|
-
if (!layoutStr && typeof body === 'string') {
|
|
146
|
+
} else if (typeof body === 'string') {
|
|
140
147
|
layoutStr = body;
|
|
141
148
|
}
|
|
149
|
+
|
|
142
150
|
if (layoutStr) {
|
|
143
151
|
const token = this._findBearerInString(layoutStr);
|
|
144
152
|
if (token) {
|
|
145
153
|
this.client.state.authorization = token;
|
|
146
154
|
try {
|
|
147
155
|
this.client.state.updateAuthorization();
|
|
148
|
-
}
|
|
149
|
-
catch (e) { }
|
|
156
|
+
} catch (e) {}
|
|
150
157
|
return token;
|
|
151
158
|
}
|
|
152
|
-
|
|
153
|
-
|
|
159
|
+
|
|
160
|
+
// Embedded login_response JSON string
|
|
161
|
+
const loginResponseMatch = layoutStr.match(
|
|
162
|
+
/"login_response"\s*:\s*"(\\?{.*?\\?})"/s
|
|
163
|
+
);
|
|
154
164
|
if (loginResponseMatch) {
|
|
155
|
-
// Unescape JSON string
|
|
156
165
|
let jsonStr = loginResponseMatch[1];
|
|
157
166
|
try {
|
|
158
|
-
jsonStr = jsonStr
|
|
167
|
+
jsonStr = jsonStr
|
|
168
|
+
.replace(/\\"/g, '"')
|
|
169
|
+
.replace(/\\n/g, '');
|
|
159
170
|
const parsed = JSON.parse(jsonStr);
|
|
160
171
|
if (parsed && parsed.headers) {
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
this.client.state.authorization =
|
|
172
|
+
const t = this._findBearerInString(parsed.headers);
|
|
173
|
+
if (t) {
|
|
174
|
+
this.client.state.authorization = t;
|
|
164
175
|
try {
|
|
165
176
|
this.client.state.updateAuthorization();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return token;
|
|
177
|
+
} catch (e) {}
|
|
178
|
+
return t;
|
|
169
179
|
}
|
|
170
180
|
}
|
|
171
|
-
}
|
|
172
|
-
catch (e) {
|
|
173
|
-
// ignore parse errors
|
|
174
|
-
}
|
|
181
|
+
} catch (e) {}
|
|
175
182
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
|
|
184
|
+
// Encryption key id + pub key in layout
|
|
185
|
+
const encKeyIdMatch =
|
|
186
|
+
layoutStr.match(
|
|
187
|
+
/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i
|
|
188
|
+
) ||
|
|
189
|
+
layoutStr.match(
|
|
190
|
+
/password.encryption.key.id[\\"\s:]+(\d+)/i
|
|
191
|
+
);
|
|
179
192
|
if (encKeyIdMatch) {
|
|
180
|
-
this.client.state.passwordEncryptionKeyId =
|
|
193
|
+
this.client.state.passwordEncryptionKeyId =
|
|
194
|
+
parseInt(encKeyIdMatch[1]);
|
|
181
195
|
}
|
|
182
|
-
|
|
183
|
-
|
|
196
|
+
|
|
197
|
+
const encPubKeyMatch =
|
|
198
|
+
layoutStr.match(
|
|
199
|
+
/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
200
|
+
) ||
|
|
201
|
+
layoutStr.match(
|
|
202
|
+
/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
203
|
+
);
|
|
184
204
|
if (encPubKeyMatch) {
|
|
185
|
-
this.client.state.passwordEncryptionPubKey =
|
|
205
|
+
this.client.state.passwordEncryptionPubKey =
|
|
206
|
+
encPubKeyMatch[1];
|
|
186
207
|
}
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
|
|
209
|
+
// User / pk id
|
|
210
|
+
const dsIdMatch =
|
|
211
|
+
layoutStr.match(
|
|
212
|
+
/ig-set-ig-u-ds-user-id[\\"\s:]+(\d+)/i
|
|
213
|
+
) ||
|
|
189
214
|
layoutStr.match(/ig-u-ds-user-id[\\"\s:]+(\d+)/i) ||
|
|
190
215
|
layoutStr.match(/"strong_id__"\s*:\s*"(\d+)"/i) ||
|
|
191
216
|
layoutStr.match(/"pk_id"\s*:\s*"(\d+)"/i) ||
|
|
@@ -195,57 +220,61 @@ class AccountRepository extends Repository {
|
|
|
195
220
|
this.client.state.cookieUserId = uid;
|
|
196
221
|
this.client.state._userId = uid;
|
|
197
222
|
}
|
|
198
|
-
|
|
223
|
+
|
|
224
|
+
const rurMatch =
|
|
225
|
+
layoutStr.match(
|
|
226
|
+
/ig-set-ig-u-rur[\\"\s:]+"([^"\\]+)/i
|
|
227
|
+
) ||
|
|
199
228
|
layoutStr.match(/ig-u-rur[\\"\s:]+"([^"\\]+)/i);
|
|
200
229
|
if (rurMatch) {
|
|
201
230
|
this.client.state.igURur = rurMatch[1];
|
|
202
231
|
}
|
|
203
|
-
|
|
232
|
+
|
|
233
|
+
const wwwClaimMatch = layoutStr.match(
|
|
234
|
+
/x-ig-set-www-claim[\\"\s:]+"([^"\\]+)/i
|
|
235
|
+
);
|
|
204
236
|
if (wwwClaimMatch) {
|
|
205
237
|
this.client.state.igWWWClaim = wwwClaimMatch[1];
|
|
206
238
|
}
|
|
207
|
-
|
|
239
|
+
|
|
240
|
+
const midMatch = layoutStr.match(
|
|
241
|
+
/"mid"\s*:\s*"([^"\\]+)"/i
|
|
242
|
+
);
|
|
208
243
|
if (midMatch) {
|
|
209
244
|
this.client.state.mid = midMatch[1];
|
|
210
245
|
}
|
|
211
246
|
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// ignore
|
|
215
|
-
}
|
|
216
|
-
// 3) If body.logged_in_user exists and we have authorization already, return it
|
|
247
|
+
} catch (e) {}
|
|
248
|
+
|
|
217
249
|
if (body.logged_in_user && this.client.state.authorization) {
|
|
218
250
|
return this.client.state.authorization;
|
|
219
251
|
}
|
|
220
252
|
}
|
|
253
|
+
|
|
221
254
|
return null;
|
|
222
255
|
}
|
|
223
|
-
|
|
256
|
+
|
|
224
257
|
_normalizeTokenString(val) {
|
|
225
|
-
if (!val || typeof val !== 'string')
|
|
226
|
-
|
|
227
|
-
// If header already contains "Bearer IGT:2:..."
|
|
258
|
+
if (!val || typeof val !== 'string') return null;
|
|
259
|
+
|
|
228
260
|
const bearer = this._findBearerInString(val);
|
|
229
|
-
if (bearer)
|
|
230
|
-
|
|
231
|
-
// If header contains JSON with IG-Set-Authorization field
|
|
261
|
+
if (bearer) return bearer;
|
|
262
|
+
|
|
232
263
|
try {
|
|
233
264
|
const maybeJson = JSON.parse(val);
|
|
234
265
|
if (maybeJson['IG-Set-Authorization']) {
|
|
235
|
-
return this._findBearerInString(
|
|
266
|
+
return this._findBearerInString(
|
|
267
|
+
maybeJson['IG-Set-Authorization']
|
|
268
|
+
);
|
|
236
269
|
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// not JSON
|
|
240
|
-
}
|
|
241
|
-
// fallback: return trimmed value
|
|
270
|
+
} catch (e) {}
|
|
271
|
+
|
|
242
272
|
return val.trim();
|
|
243
273
|
}
|
|
244
|
-
|
|
274
|
+
|
|
245
275
|
_findBearerInString(str) {
|
|
246
|
-
if (!str || typeof str !== 'string')
|
|
247
|
-
|
|
248
|
-
// Patterns to match the Bearer token in many possible encodings/escapes
|
|
276
|
+
if (!str || typeof str !== 'string') return null;
|
|
277
|
+
|
|
249
278
|
const bearerPatterns = [
|
|
250
279
|
/Bearer IGT:2:[A-Za-z0-9+\/=]+/,
|
|
251
280
|
/Bearer\s+IGT:2:[A-Za-z0-9+\/=]+/,
|
|
@@ -255,98 +284,378 @@ class AccountRepository extends Repository {
|
|
|
255
284
|
/IG-Set-Authorization[\s:]+(Bearer IGT:2:[A-Za-z0-9+\/=]+)/i,
|
|
256
285
|
/Bearer IGT:2:([A-Za-z0-9+\/=]+)/,
|
|
257
286
|
];
|
|
287
|
+
|
|
258
288
|
for (const pattern of bearerPatterns) {
|
|
259
289
|
const m = str.match(pattern);
|
|
260
290
|
if (m) {
|
|
261
|
-
// If capturing group present, prefer it
|
|
262
291
|
if (m[1]) {
|
|
263
|
-
const token = m[1].includes('Bearer IGT:2:')
|
|
292
|
+
const token = m[1].includes('Bearer IGT:2:')
|
|
293
|
+
? m[1]
|
|
294
|
+
: `Bearer IGT:2:${m[1]}`;
|
|
264
295
|
return token.replace(/\?"/g, '').trim();
|
|
265
296
|
}
|
|
266
|
-
// Otherwise m[0] contains the full token
|
|
267
297
|
return m[0].replace(/\?"/g, '').trim();
|
|
268
298
|
}
|
|
269
299
|
}
|
|
300
|
+
|
|
270
301
|
return null;
|
|
271
302
|
}
|
|
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
|
+
|
|
272
332
|
// Ensure we have a valid csrftoken cookie (and mid) before sensitive endpoints.
|
|
273
|
-
// Instagram typically sets csrftoken via /api/v1/si/fetch_headers/.
|
|
274
333
|
async ensureCsrfToken() {
|
|
275
334
|
const cookieToken = this.client.state.cookieCsrfToken;
|
|
276
|
-
if (
|
|
335
|
+
if (
|
|
336
|
+
cookieToken &&
|
|
337
|
+
cookieToken !== 'missing' &&
|
|
338
|
+
cookieToken !== 'pending'
|
|
339
|
+
) {
|
|
277
340
|
try {
|
|
278
341
|
this.client.state.csrfToken = cookieToken;
|
|
279
|
-
}
|
|
280
|
-
catch { }
|
|
342
|
+
} catch {}
|
|
281
343
|
return cookieToken;
|
|
282
344
|
}
|
|
283
|
-
|
|
345
|
+
|
|
284
346
|
try {
|
|
285
347
|
await this.client.request.send({
|
|
286
348
|
method: 'GET',
|
|
287
349
|
url: `/api/v1/si/fetch_headers/?challenge_type=signup&guid=${this.client.state.uuid}`,
|
|
288
350
|
});
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// ignore warmup failures
|
|
292
|
-
}
|
|
351
|
+
} catch (e) {}
|
|
352
|
+
|
|
293
353
|
const token = this.client.state.cookieCsrfToken;
|
|
294
|
-
if (
|
|
354
|
+
if (
|
|
355
|
+
token &&
|
|
356
|
+
token !== 'missing' &&
|
|
357
|
+
token !== 'pending'
|
|
358
|
+
) {
|
|
295
359
|
try {
|
|
296
360
|
this.client.state.csrfToken = token;
|
|
297
|
-
}
|
|
298
|
-
catch { }
|
|
361
|
+
} catch {}
|
|
299
362
|
return token;
|
|
300
363
|
}
|
|
364
|
+
|
|
301
365
|
return null;
|
|
302
366
|
}
|
|
367
|
+
|
|
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
|
+
async _prefetchOauthTokenForLogin(username) {
|
|
373
|
+
if (!username) return null;
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
377
|
+
|
|
378
|
+
const androidDeviceId =
|
|
379
|
+
this.client.state.androidDeviceId ||
|
|
380
|
+
this.client.state.deviceId ||
|
|
381
|
+
`android-${crypto.randomBytes(8).toString('hex')}`;
|
|
382
|
+
|
|
383
|
+
const familyDeviceId =
|
|
384
|
+
this.client.state.phoneId ||
|
|
385
|
+
this.client.state.familyDeviceId ||
|
|
386
|
+
(crypto.randomUUID
|
|
387
|
+
? crypto.randomUUID()
|
|
388
|
+
: require('uuid').v4());
|
|
389
|
+
|
|
390
|
+
const qeDeviceId =
|
|
391
|
+
this.client.state.deviceId ||
|
|
392
|
+
this.client.state.qeDeviceId ||
|
|
393
|
+
(crypto.randomUUID
|
|
394
|
+
? crypto.randomUUID()
|
|
395
|
+
: require('uuid').v4());
|
|
396
|
+
|
|
397
|
+
const waterfallId = crypto.randomUUID
|
|
398
|
+
? crypto.randomUUID()
|
|
399
|
+
: require('uuid').v4();
|
|
400
|
+
|
|
401
|
+
const latencyMarkerId = 36707139;
|
|
402
|
+
const latencyInstanceId = Number(
|
|
403
|
+
`${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const aacInitTimestamp =
|
|
407
|
+
nowSec - Math.floor(Math.random() * 50);
|
|
408
|
+
const aacjid = crypto.randomUUID
|
|
409
|
+
? crypto.randomUUID()
|
|
410
|
+
: require('uuid').v4();
|
|
411
|
+
const aaccs = crypto
|
|
412
|
+
.randomBytes(32)
|
|
413
|
+
.toString('base64')
|
|
414
|
+
.replace(/=/g, '');
|
|
415
|
+
|
|
416
|
+
const clientInputParams = {
|
|
417
|
+
username_input: username,
|
|
418
|
+
aac: JSON.stringify({
|
|
419
|
+
aac_init_timestamp: aacInitTimestamp,
|
|
420
|
+
aacjid,
|
|
421
|
+
aaccs,
|
|
422
|
+
}),
|
|
423
|
+
lois_settings: { lois_token: '' },
|
|
424
|
+
cloud_trust_token: null,
|
|
425
|
+
zero_balance_state: '',
|
|
426
|
+
network_bssid: null,
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const serverParams = {
|
|
430
|
+
is_from_logged_out: 0,
|
|
431
|
+
layered_homepage_experiment_group: null,
|
|
432
|
+
device_id: androidDeviceId,
|
|
433
|
+
login_surface: 'switcher',
|
|
434
|
+
waterfall_id: waterfallId,
|
|
435
|
+
INTERNAL__latency_qpl_instance_id: latencyInstanceId,
|
|
436
|
+
is_platform_login: 0,
|
|
437
|
+
INTERNAL__latency_qpl_marker_id: latencyMarkerId,
|
|
438
|
+
family_device_id: familyDeviceId,
|
|
439
|
+
offline_experiment_group: 'caa_iteration_v3_perf_ig_4',
|
|
440
|
+
access_flow_version: 'pre_mt_behavior',
|
|
441
|
+
is_from_logged_in_switcher: 1,
|
|
442
|
+
qe_device_id: qeDeviceId,
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const paramsJson = JSON.stringify({
|
|
446
|
+
client_input_params: clientInputParams,
|
|
447
|
+
server_params: serverParams,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const bloksVersionId =
|
|
451
|
+
this.client.state.bloksVersionId ||
|
|
452
|
+
'5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
|
|
453
|
+
|
|
454
|
+
const bkClientContext = JSON.stringify({
|
|
455
|
+
bloks_version: bloksVersionId,
|
|
456
|
+
styles_id: 'instagram',
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const lang = this.client.state.language || 'ro_RO';
|
|
460
|
+
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
461
|
+
const userAgent = this._resolveUserAgent();
|
|
462
|
+
|
|
463
|
+
const timezoneOffset =
|
|
464
|
+
typeof this.client.state.timezoneOffset === 'number'
|
|
465
|
+
? this.client.state.timezoneOffset
|
|
466
|
+
: 7200;
|
|
467
|
+
|
|
468
|
+
const bloksHeaders = {
|
|
469
|
+
'accept-language': acceptLanguage,
|
|
470
|
+
'content-type':
|
|
471
|
+
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
472
|
+
'ig-intended-user-id': '0',
|
|
473
|
+
priority: 'u=3',
|
|
474
|
+
|
|
475
|
+
'x-bloks-is-layout-rtl': 'false',
|
|
476
|
+
'x-bloks-prism-ax-base-colors-enabled': 'false',
|
|
477
|
+
'x-bloks-prism-button-version': 'CONTROL',
|
|
478
|
+
'x-bloks-prism-colors-enabled': 'true',
|
|
479
|
+
'x-bloks-prism-font-enabled': 'false',
|
|
480
|
+
'x-bloks-prism-indigo-link-version': '0',
|
|
481
|
+
'x-bloks-version-id': bloksVersionId,
|
|
482
|
+
|
|
483
|
+
'x-ig-android-id': androidDeviceId,
|
|
484
|
+
'x-ig-device-id': qeDeviceId,
|
|
485
|
+
'x-ig-family-device-id': familyDeviceId,
|
|
486
|
+
'x-ig-timezone-offset': String(timezoneOffset),
|
|
487
|
+
'x-ig-app-id': String(
|
|
488
|
+
this.client.state.fbAnalyticsApplicationId ||
|
|
489
|
+
'567067343352427'
|
|
490
|
+
),
|
|
491
|
+
'x-ig-app-locale': lang,
|
|
492
|
+
'x-ig-device-locale': lang,
|
|
493
|
+
'x-ig-mapped-locale': lang,
|
|
494
|
+
|
|
495
|
+
'x-ig-client-endpoint':
|
|
496
|
+
'com.bloks.www.caa.login.login_homepage',
|
|
497
|
+
'x-ig-nav-chain':
|
|
498
|
+
'com.bloks.www.caa.login.login_homepage:com.bloks.www.caa.login.login_homepage:1:button:0:::0',
|
|
499
|
+
|
|
500
|
+
'x-ig-connection-type': 'WIFI',
|
|
501
|
+
'x-ig-capabilities': '3brTv10=',
|
|
502
|
+
'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')}`,
|
|
509
|
+
'x-pigeon-session-id':
|
|
510
|
+
this.client.state.pigeonSessionId ||
|
|
511
|
+
`UFS-${crypto.randomBytes(16).toString('hex')}-2`,
|
|
512
|
+
|
|
513
|
+
'x-mid':
|
|
514
|
+
this.client.state.mid ||
|
|
515
|
+
`aZ${crypto.randomBytes(8).toString('hex')}`,
|
|
516
|
+
'x-tigon-is-retry': 'False',
|
|
517
|
+
|
|
518
|
+
'x-fb-client-ip': 'True',
|
|
519
|
+
'x-fb-connection-type': 'WIFI',
|
|
520
|
+
'x-fb-server-cluster': 'True',
|
|
521
|
+
'x-fb-network-properties':
|
|
522
|
+
'VPN;Validated;LocalAddrs=/10.0.0.2,;',
|
|
523
|
+
'x-fb-request-analytics-tags': JSON.stringify({
|
|
524
|
+
network_tags: {
|
|
525
|
+
product: String(
|
|
526
|
+
this.client.state.fbAnalyticsApplicationId ||
|
|
527
|
+
'567067343352427'
|
|
528
|
+
),
|
|
529
|
+
purpose: 'fetch',
|
|
530
|
+
surface: 'undefined',
|
|
531
|
+
request_category: 'api',
|
|
532
|
+
retry_attempt: '0',
|
|
533
|
+
},
|
|
534
|
+
}),
|
|
535
|
+
'x-fb-friendly-name':
|
|
536
|
+
'IgApi: bloks/async_action/com.bloks.www.caa.login.oauth.token.fetch.async/',
|
|
537
|
+
'x-fb-http-engine': 'MNS/TCP',
|
|
538
|
+
'x-fb-rmd': 'state=URL_ELIGIBLE',
|
|
539
|
+
|
|
540
|
+
'user-agent': userAgent,
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const response = await this.client.request.send({
|
|
544
|
+
method: 'POST',
|
|
545
|
+
url: '/api/v1/bloks/async_action/com.bloks.www.caa.login.oauth.token.fetch.async/',
|
|
546
|
+
form: {
|
|
547
|
+
params: paramsJson,
|
|
548
|
+
bk_client_context: bkClientContext,
|
|
549
|
+
bloks_versioning_id: bloksVersionId,
|
|
550
|
+
},
|
|
551
|
+
headers: bloksHeaders,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
const fs = require('fs');
|
|
556
|
+
const path = require('path');
|
|
557
|
+
const debugDir = path.join(
|
|
558
|
+
process.cwd(),
|
|
559
|
+
'authinfo_instagram'
|
|
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) {}
|
|
582
|
+
|
|
583
|
+
return response.body;
|
|
584
|
+
} catch (e) {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
303
589
|
async login(credentialsOrUsername, passwordArg) {
|
|
304
590
|
let username, password;
|
|
305
|
-
if (
|
|
591
|
+
if (
|
|
592
|
+
typeof credentialsOrUsername === 'object' &&
|
|
593
|
+
credentialsOrUsername !== null
|
|
594
|
+
) {
|
|
306
595
|
username = credentialsOrUsername.username;
|
|
307
596
|
password = credentialsOrUsername.password;
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
597
|
+
} else {
|
|
310
598
|
username = credentialsOrUsername;
|
|
311
599
|
password = passwordArg;
|
|
312
600
|
}
|
|
601
|
+
|
|
313
602
|
if (!username || !password) {
|
|
314
603
|
throw new Error('Username and password are required');
|
|
315
604
|
}
|
|
605
|
+
|
|
316
606
|
if (!this.client.state.passwordEncryptionPubKey) {
|
|
317
607
|
await this.syncLoginExperiments();
|
|
318
608
|
}
|
|
609
|
+
|
|
319
610
|
const { encrypted, time } = this.encryptPassword(password);
|
|
320
|
-
|
|
611
|
+
|
|
321
612
|
await this.ensureCsrfToken();
|
|
613
|
+
|
|
614
|
+
try {
|
|
615
|
+
await this._prefetchOauthTokenForLogin(username);
|
|
616
|
+
} catch (e) {}
|
|
617
|
+
|
|
322
618
|
return this.requestWithRetry(async () => {
|
|
323
|
-
// ====== client_input_params (oglindă după traficul real) ======
|
|
324
619
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
325
|
-
const aacInitTimestamp =
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
620
|
+
const aacInitTimestamp =
|
|
621
|
+
nowSec - Math.floor(Math.random() * 50);
|
|
622
|
+
const aacjid = crypto.randomUUID
|
|
623
|
+
? crypto.randomUUID()
|
|
624
|
+
: require('uuid').v4();
|
|
625
|
+
const aaccs = crypto
|
|
626
|
+
.randomBytes(32)
|
|
627
|
+
.toString('base64')
|
|
628
|
+
.replace(/=/g, '');
|
|
629
|
+
|
|
630
|
+
const androidDeviceId =
|
|
631
|
+
this.client.state.androidDeviceId ||
|
|
329
632
|
this.client.state.deviceId ||
|
|
330
633
|
`android-${crypto.randomBytes(8).toString('hex')}`;
|
|
331
|
-
const machineId =
|
|
634
|
+
const machineId =
|
|
635
|
+
this.client.state.mid ||
|
|
332
636
|
this.client.state.machineId ||
|
|
333
637
|
`aZ${crypto.randomBytes(8).toString('hex')}`;
|
|
334
|
-
const familyDeviceId =
|
|
638
|
+
const familyDeviceId =
|
|
639
|
+
this.client.state.phoneId ||
|
|
335
640
|
this.client.state.familyDeviceId ||
|
|
336
|
-
crypto.randomUUID
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const qeDeviceId =
|
|
641
|
+
(crypto.randomUUID
|
|
642
|
+
? crypto.randomUUID()
|
|
643
|
+
: require('uuid').v4());
|
|
644
|
+
const qeDeviceId =
|
|
645
|
+
this.client.state.deviceId ||
|
|
340
646
|
this.client.state.qeDeviceId ||
|
|
341
|
-
crypto.randomUUID
|
|
342
|
-
|
|
343
|
-
|
|
647
|
+
(crypto.randomUUID
|
|
648
|
+
? crypto.randomUUID()
|
|
649
|
+
: require('uuid').v4());
|
|
650
|
+
|
|
344
651
|
const accountsList = this.client.state.accountsList || [];
|
|
652
|
+
|
|
345
653
|
const flashCallPermissionStatus = {
|
|
346
654
|
READ_PHONE_STATE: 'GRANTED',
|
|
347
655
|
READ_CALL_LOG: 'GRANTED',
|
|
348
656
|
ANSWER_PHONE_CALLS: 'GRANTED',
|
|
349
657
|
};
|
|
658
|
+
|
|
350
659
|
const clientInputParams = {
|
|
351
660
|
aac: JSON.stringify({
|
|
352
661
|
aac_init_timestamp: aacInitTimestamp,
|
|
@@ -380,7 +689,8 @@ class AccountRepository extends Repository {
|
|
|
380
689
|
machine_id: machineId,
|
|
381
690
|
flash_call_permission_status: flashCallPermissionStatus,
|
|
382
691
|
accounts_list: accountsList,
|
|
383
|
-
gms_incoming_call_retriever_eligibility:
|
|
692
|
+
gms_incoming_call_retriever_eligibility:
|
|
693
|
+
'client_not_supported',
|
|
384
694
|
family_device_id: familyDeviceId,
|
|
385
695
|
fb_ig_device_id: [],
|
|
386
696
|
device_emails: [],
|
|
@@ -391,10 +701,15 @@ class AccountRepository extends Repository {
|
|
|
391
701
|
openid_tokens: {},
|
|
392
702
|
contact_point: username,
|
|
393
703
|
};
|
|
394
|
-
|
|
395
|
-
const waterfallId = crypto.randomUUID
|
|
704
|
+
|
|
705
|
+
const waterfallId = crypto.randomUUID
|
|
706
|
+
? crypto.randomUUID()
|
|
707
|
+
: require('uuid').v4();
|
|
396
708
|
const latencyMarkerId = 36707139;
|
|
397
|
-
const latencyInstanceId = Number(
|
|
709
|
+
const latencyInstanceId = Number(
|
|
710
|
+
`${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
711
|
+
);
|
|
712
|
+
|
|
398
713
|
const serverParams = {
|
|
399
714
|
should_trigger_override_login_2fa_action: 0,
|
|
400
715
|
is_vanilla_password_page_empty_password: 0,
|
|
@@ -408,7 +723,8 @@ class AccountRepository extends Repository {
|
|
|
408
723
|
is_platform_login: 0,
|
|
409
724
|
INTERNAL__latency_qpl_marker_id: latencyMarkerId,
|
|
410
725
|
is_from_aymh: 0,
|
|
411
|
-
offline_experiment_group:
|
|
726
|
+
offline_experiment_group:
|
|
727
|
+
'caa_iteration_v3_perf_ig_4',
|
|
412
728
|
is_from_landing_page: 0,
|
|
413
729
|
left_nav_button_action: 'NONE',
|
|
414
730
|
password_text_input_id: 'z0jejq:194',
|
|
@@ -421,7 +737,8 @@ class AccountRepository extends Repository {
|
|
|
421
737
|
device_id: androidDeviceId,
|
|
422
738
|
login_surface: 'switcher',
|
|
423
739
|
INTERNAL__latency_qpl_instance_id: latencyInstanceId,
|
|
424
|
-
reg_flow_source:
|
|
740
|
+
reg_flow_source:
|
|
741
|
+
'login_home_native_integration_point',
|
|
425
742
|
is_caa_perf_enabled: 1,
|
|
426
743
|
credential_type: 'password',
|
|
427
744
|
is_from_password_entry_page: 0,
|
|
@@ -431,39 +748,56 @@ class AccountRepository extends Repository {
|
|
|
431
748
|
access_flow_version: 'pre_mt_behavior',
|
|
432
749
|
is_from_logged_in_switcher: 1,
|
|
433
750
|
};
|
|
751
|
+
|
|
434
752
|
const paramsJson = JSON.stringify({
|
|
435
753
|
client_input_params: clientInputParams,
|
|
436
754
|
server_params: serverParams,
|
|
437
755
|
});
|
|
438
|
-
|
|
756
|
+
|
|
757
|
+
const attestParams =
|
|
758
|
+
AccountRepository.generateAttestParams(
|
|
759
|
+
this.client.state
|
|
760
|
+
);
|
|
761
|
+
const bloksVersionId =
|
|
762
|
+
this.client.state.bloksVersionId ||
|
|
763
|
+
'5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
|
|
764
|
+
|
|
439
765
|
const bkClientContext = JSON.stringify({
|
|
440
|
-
bloks_version:
|
|
766
|
+
bloks_version: bloksVersionId,
|
|
441
767
|
styles_id: 'instagram',
|
|
442
768
|
});
|
|
443
|
-
|
|
444
|
-
const lang =
|
|
769
|
+
|
|
770
|
+
const lang = this.client.state.language || 'ro_RO';
|
|
445
771
|
const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
|
|
446
|
-
const userAgent = this.
|
|
447
|
-
|
|
772
|
+
const userAgent = this._resolveUserAgent();
|
|
773
|
+
|
|
448
774
|
const bloksHeaders = {
|
|
449
775
|
'accept-language': acceptLanguage,
|
|
450
|
-
'content-type':
|
|
776
|
+
'content-type':
|
|
777
|
+
'application/x-www-form-urlencoded; charset=UTF-8',
|
|
451
778
|
'ig-intended-user-id': '0',
|
|
452
|
-
|
|
779
|
+
priority: 'u=3',
|
|
780
|
+
|
|
453
781
|
'x-bloks-is-layout-rtl': 'false',
|
|
454
782
|
'x-bloks-prism-ax-base-colors-enabled': 'false',
|
|
455
783
|
'x-bloks-prism-button-version': 'CONTROL',
|
|
456
784
|
'x-bloks-prism-colors-enabled': 'true',
|
|
457
785
|
'x-bloks-prism-font-enabled': 'false',
|
|
458
786
|
'x-bloks-prism-indigo-link-version': '0',
|
|
459
|
-
'x-bloks-version-id':
|
|
787
|
+
'x-bloks-version-id': bloksVersionId,
|
|
788
|
+
|
|
460
789
|
'x-fb-client-ip': 'True',
|
|
461
790
|
'x-fb-connection-type': 'WIFI',
|
|
462
|
-
'x-fb-friendly-name':
|
|
463
|
-
|
|
791
|
+
'x-fb-friendly-name':
|
|
792
|
+
'IgApi: bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
|
|
793
|
+
'x-fb-network-properties':
|
|
794
|
+
'VPN;Validated;LocalAddrs=/10.0.0.2,;',
|
|
464
795
|
'x-fb-request-analytics-tags': JSON.stringify({
|
|
465
796
|
network_tags: {
|
|
466
|
-
product: String(
|
|
797
|
+
product: String(
|
|
798
|
+
this.client.state.fbAnalyticsApplicationId ||
|
|
799
|
+
'567067343352427'
|
|
800
|
+
),
|
|
467
801
|
purpose: 'fetch',
|
|
468
802
|
surface: 'undefined',
|
|
469
803
|
request_category: 'api',
|
|
@@ -471,78 +805,107 @@ class AccountRepository extends Repository {
|
|
|
471
805
|
},
|
|
472
806
|
}),
|
|
473
807
|
'x-fb-server-cluster': 'True',
|
|
808
|
+
|
|
474
809
|
'x-ig-android-id': androidDeviceId,
|
|
475
|
-
'x-ig-app-id': String(
|
|
810
|
+
'x-ig-app-id': String(
|
|
811
|
+
this.client.state.fbAnalyticsApplicationId ||
|
|
812
|
+
'567067343352427'
|
|
813
|
+
),
|
|
476
814
|
'x-ig-app-locale': lang,
|
|
477
815
|
'x-ig-attest-params': JSON.stringify(attestParams),
|
|
478
|
-
'x-ig-bandwidth-speed-kbps': (
|
|
816
|
+
'x-ig-bandwidth-speed-kbps': (
|
|
817
|
+
Math.random() * 1500 +
|
|
818
|
+
800
|
|
819
|
+
).toFixed(3),
|
|
479
820
|
'x-ig-bandwidth-totalbytes-b': '0',
|
|
480
821
|
'x-ig-bandwidth-totaltime-ms': '0',
|
|
481
|
-
'x-ig-client-endpoint':
|
|
822
|
+
'x-ig-client-endpoint':
|
|
823
|
+
'com.bloks.www.caa.login.aymh_single_profile_screen_entry',
|
|
482
824
|
'x-ig-capabilities': '3brTv10=',
|
|
483
825
|
'x-ig-connection-type': 'WIFI',
|
|
484
826
|
'x-ig-device-id': qeDeviceId,
|
|
485
827
|
'x-ig-device-locale': lang,
|
|
486
828
|
'x-ig-family-device-id': familyDeviceId,
|
|
487
829
|
'x-ig-mapped-locale': lang,
|
|
488
|
-
'x-ig-nav-chain':
|
|
489
|
-
'com.bloks.www.caa.login.aymh_single_profile_screen_entry:com.bloks.www.caa.login.aymh_single_profile_screen_entry:14:button:0:::0',
|
|
490
|
-
'x-ig-timezone-offset': String(
|
|
491
|
-
|
|
492
|
-
|
|
830
|
+
'x-ig-nav-chain':
|
|
831
|
+
'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',
|
|
832
|
+
'x-ig-timezone-offset': String(
|
|
833
|
+
typeof this.client.state.timezoneOffset ===
|
|
834
|
+
'number'
|
|
835
|
+
? this.client.state.timezoneOffset
|
|
836
|
+
: 7200
|
|
837
|
+
),
|
|
493
838
|
'x-ig-www-claim': this.client.state.igWWWClaim || '0',
|
|
494
839
|
'x-mid': machineId,
|
|
495
|
-
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
|
|
840
|
+
'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
|
|
841
|
+
Math.random() * 1000
|
|
842
|
+
)
|
|
496
843
|
.toString()
|
|
497
844
|
.padStart(3, '0')}`,
|
|
498
|
-
'x-pigeon-session-id':
|
|
499
|
-
|
|
845
|
+
'x-pigeon-session-id':
|
|
846
|
+
this.client.state.pigeonSessionId ||
|
|
847
|
+
`UFS-${crypto.randomBytes(16).toString(
|
|
848
|
+
'hex'
|
|
849
|
+
)}-1`,
|
|
500
850
|
'x-tigon-is-retry': 'False',
|
|
501
|
-
|
|
851
|
+
|
|
502
852
|
'accept-encoding': 'gzip, deflate, br',
|
|
503
|
-
// ...
|
|
504
853
|
'user-agent': userAgent,
|
|
505
|
-
'x-fb-conn-uuid-client':
|
|
854
|
+
'x-fb-conn-uuid-client':
|
|
855
|
+
crypto.randomBytes(16).toString('hex'),
|
|
506
856
|
'x-fb-http-engine': 'MNS/TCP',
|
|
507
857
|
'x-fb-rmd': 'state=URL_ELIGIBLE',
|
|
508
858
|
};
|
|
859
|
+
|
|
509
860
|
const response = await this.client.request.send({
|
|
510
861
|
method: 'POST',
|
|
511
862
|
url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
|
|
512
863
|
form: {
|
|
513
864
|
params: paramsJson,
|
|
514
865
|
bk_client_context: bkClientContext,
|
|
515
|
-
bloks_versioning_id:
|
|
866
|
+
bloks_versioning_id: bloksVersionId,
|
|
516
867
|
},
|
|
517
868
|
headers: bloksHeaders,
|
|
518
869
|
});
|
|
519
|
-
|
|
870
|
+
|
|
871
|
+
// DEBUG login response
|
|
520
872
|
try {
|
|
521
873
|
const fs = require('fs');
|
|
522
874
|
const path = require('path');
|
|
523
|
-
const debugDir = path.join(
|
|
524
|
-
|
|
875
|
+
const debugDir = path.join(
|
|
876
|
+
process.cwd(),
|
|
877
|
+
'authinfo_instagram'
|
|
878
|
+
);
|
|
879
|
+
const debugFile = path.join(
|
|
880
|
+
debugDir,
|
|
881
|
+
'login-debug.json'
|
|
882
|
+
);
|
|
525
883
|
try {
|
|
526
884
|
fs.mkdirSync(debugDir, { recursive: true });
|
|
527
|
-
}
|
|
528
|
-
|
|
885
|
+
} catch (e) {}
|
|
886
|
+
|
|
529
887
|
const debugPayload = {
|
|
530
888
|
at: new Date().toISOString(),
|
|
531
|
-
statusCode:
|
|
889
|
+
statusCode:
|
|
890
|
+
response.statusCode || response.status || null,
|
|
532
891
|
headers: response.headers || null,
|
|
533
892
|
body: response.body || null,
|
|
534
893
|
};
|
|
535
|
-
fs.writeFileSync(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
894
|
+
fs.writeFileSync(
|
|
895
|
+
debugFile,
|
|
896
|
+
JSON.stringify(debugPayload, null, 2),
|
|
897
|
+
'utf8'
|
|
898
|
+
);
|
|
899
|
+
} catch (e) {}
|
|
900
|
+
|
|
541
901
|
const body = response.body;
|
|
542
|
-
|
|
902
|
+
|
|
543
903
|
this._extractAndSaveAuthorization(response);
|
|
904
|
+
|
|
544
905
|
if (body && body.two_factor_required) {
|
|
545
|
-
const err = new Error(
|
|
906
|
+
const err = new Error(
|
|
907
|
+
'Two factor authentication required'
|
|
908
|
+
);
|
|
546
909
|
err.name = 'IgLoginTwoFactorRequiredError';
|
|
547
910
|
err.twoFactorInfo = body.two_factor_info;
|
|
548
911
|
throw err;
|
|
@@ -563,28 +926,45 @@ class AccountRepository extends Repository {
|
|
|
563
926
|
err.challengeInfo = body.challenge;
|
|
564
927
|
throw err;
|
|
565
928
|
}
|
|
929
|
+
|
|
566
930
|
if (body && body.layout) {
|
|
567
|
-
const layoutStr =
|
|
568
|
-
|
|
569
|
-
|
|
931
|
+
const layoutStr =
|
|
932
|
+
typeof body.layout === 'string'
|
|
933
|
+
? body.layout
|
|
934
|
+
: JSON.stringify(body.layout);
|
|
935
|
+
|
|
936
|
+
if (
|
|
937
|
+
layoutStr.includes('two_factor_required') ||
|
|
938
|
+
layoutStr.includes('"two_factor_info"')
|
|
939
|
+
) {
|
|
570
940
|
let twoFactorInfo = null;
|
|
571
941
|
try {
|
|
572
|
-
const match = layoutStr.match(
|
|
942
|
+
const match = layoutStr.match(
|
|
943
|
+
/"two_factor_info"\s*:\s*(\{[^}]+\})/
|
|
944
|
+
);
|
|
573
945
|
if (match)
|
|
574
946
|
twoFactorInfo = JSON.parse(match[1]);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
|
|
947
|
+
} catch (e) {}
|
|
948
|
+
const err = new Error(
|
|
949
|
+
'Two factor authentication required'
|
|
950
|
+
);
|
|
578
951
|
err.name = 'IgLoginTwoFactorRequiredError';
|
|
579
952
|
err.twoFactorInfo = twoFactorInfo;
|
|
580
953
|
throw err;
|
|
581
954
|
}
|
|
582
|
-
|
|
955
|
+
|
|
956
|
+
if (
|
|
957
|
+
layoutStr.includes('bad_password') ||
|
|
958
|
+
layoutStr.includes('incorrect_password')
|
|
959
|
+
) {
|
|
583
960
|
const err = new Error('Bad password');
|
|
584
961
|
err.name = 'IgLoginBadPasswordError';
|
|
585
962
|
throw err;
|
|
586
963
|
}
|
|
587
|
-
if (
|
|
964
|
+
if (
|
|
965
|
+
layoutStr.includes('invalid_user') ||
|
|
966
|
+
layoutStr.includes('user_not_found')
|
|
967
|
+
) {
|
|
588
968
|
const err = new Error('Invalid user');
|
|
589
969
|
err.name = 'IgLoginInvalidUserError';
|
|
590
970
|
throw err;
|
|
@@ -595,16 +975,17 @@ class AccountRepository extends Repository {
|
|
|
595
975
|
err.challengeInfo = body.challenge || null;
|
|
596
976
|
throw err;
|
|
597
977
|
}
|
|
598
|
-
|
|
599
|
-
const tokenFromLayout =
|
|
978
|
+
|
|
979
|
+
const tokenFromLayout =
|
|
980
|
+
this._findBearerInString(layoutStr);
|
|
600
981
|
if (tokenFromLayout) {
|
|
601
|
-
this.client.state.authorization =
|
|
982
|
+
this.client.state.authorization =
|
|
983
|
+
tokenFromLayout;
|
|
602
984
|
try {
|
|
603
985
|
this.client.state.updateAuthorization();
|
|
604
|
-
}
|
|
605
|
-
catch (e) { }
|
|
986
|
+
} catch (e) {}
|
|
606
987
|
}
|
|
607
|
-
|
|
988
|
+
|
|
608
989
|
const pkPatterns = [
|
|
609
990
|
/\\"pk\\":\s*(\d+)/,
|
|
610
991
|
/"pk":\s*(\d+)/,
|
|
@@ -618,12 +999,13 @@ class AccountRepository extends Repository {
|
|
|
618
999
|
const pkMatch = layoutStr.match(pattern);
|
|
619
1000
|
if (pkMatch) {
|
|
620
1001
|
extractedPk = pkMatch[1];
|
|
621
|
-
this.client.state.cookieUserId =
|
|
1002
|
+
this.client.state.cookieUserId =
|
|
1003
|
+
extractedPk;
|
|
622
1004
|
this.client.state._userId = extractedPk;
|
|
623
1005
|
break;
|
|
624
1006
|
}
|
|
625
1007
|
}
|
|
626
|
-
|
|
1008
|
+
|
|
627
1009
|
const wwwClaimPatterns = [
|
|
628
1010
|
/IG-Set-WWW-Claim[\\"\s:]+([a-f0-9]+)/i,
|
|
629
1011
|
/ig-set-www-claim[\\"\s:]+([a-f0-9]+)/i,
|
|
@@ -633,37 +1015,60 @@ class AccountRepository extends Repository {
|
|
|
633
1015
|
for (const pattern of wwwClaimPatterns) {
|
|
634
1016
|
const wwwClaimMatch = layoutStr.match(pattern);
|
|
635
1017
|
if (wwwClaimMatch) {
|
|
636
|
-
this.client.state.igWWWClaim =
|
|
1018
|
+
this.client.state.igWWWClaim =
|
|
1019
|
+
wwwClaimMatch[1];
|
|
637
1020
|
break;
|
|
638
1021
|
}
|
|
639
1022
|
}
|
|
640
|
-
|
|
641
|
-
const
|
|
642
|
-
layoutStr.match(
|
|
643
|
-
|
|
644
|
-
|
|
1023
|
+
|
|
1024
|
+
const encKeyIdMatch2 =
|
|
1025
|
+
layoutStr.match(
|
|
1026
|
+
/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i
|
|
1027
|
+
) ||
|
|
1028
|
+
layoutStr.match(
|
|
1029
|
+
/password.encryption.key.id[\\"\s:]+(\d+)/i
|
|
1030
|
+
);
|
|
1031
|
+
if (encKeyIdMatch2) {
|
|
1032
|
+
this.client.state.passwordEncryptionKeyId =
|
|
1033
|
+
parseInt(encKeyIdMatch2[1]);
|
|
645
1034
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
1035
|
+
|
|
1036
|
+
const encPubKeyMatch2 =
|
|
1037
|
+
layoutStr.match(
|
|
1038
|
+
/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
1039
|
+
) ||
|
|
1040
|
+
layoutStr.match(
|
|
1041
|
+
/password.encryption.pub.key[\\"\s:]+([A-Za-z0-9+\/=]+)/i
|
|
1042
|
+
);
|
|
1043
|
+
if (encPubKeyMatch2) {
|
|
1044
|
+
this.client.state.passwordEncryptionPubKey =
|
|
1045
|
+
encPubKeyMatch2[1];
|
|
650
1046
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1047
|
+
|
|
1048
|
+
const midMatch2 =
|
|
1049
|
+
layoutStr.match(
|
|
1050
|
+
/ig-set-x-mid[\\"\s:]+([A-Za-z0-9+\/=_-]+)/i
|
|
1051
|
+
) ||
|
|
1052
|
+
layoutStr.match(
|
|
1053
|
+
/\\"x-mid\\"[\\"\s:]+([A-Za-z0-9+\/=_-]+)/i
|
|
1054
|
+
);
|
|
1055
|
+
if (midMatch2) {
|
|
1056
|
+
this.client.state.mid = midMatch2[1];
|
|
655
1057
|
}
|
|
656
|
-
|
|
1058
|
+
|
|
657
1059
|
const loginResponsePatterns = [
|
|
658
1060
|
/\\"logged_in_user\\":\{[^}]*\\"pk\\":(\d+)[^}]*\\"username\\":\\"([^"\\]+)\\"/,
|
|
659
1061
|
/"logged_in_user":\{[^}]*"pk":(\d+)[^}]*"username":"([^"]+)"/,
|
|
660
1062
|
/\\"logged_in_user\\".*?\\"pk\\":\s*(\d+).*?\\"username\\":\s*\\"([^"\\]+)\\"/s,
|
|
661
1063
|
];
|
|
662
1064
|
for (const pattern of loginResponsePatterns) {
|
|
663
|
-
const loginResponseMatch =
|
|
1065
|
+
const loginResponseMatch =
|
|
1066
|
+
layoutStr.match(pattern);
|
|
664
1067
|
if (loginResponseMatch) {
|
|
665
|
-
this.client.state.cookieUserId =
|
|
666
|
-
|
|
1068
|
+
this.client.state.cookieUserId =
|
|
1069
|
+
loginResponseMatch[1];
|
|
1070
|
+
this.client.state._userId =
|
|
1071
|
+
loginResponseMatch[1];
|
|
667
1072
|
return {
|
|
668
1073
|
pk: parseInt(loginResponseMatch[1]),
|
|
669
1074
|
pk_id: loginResponseMatch[1],
|
|
@@ -671,53 +1076,65 @@ class AccountRepository extends Repository {
|
|
|
671
1076
|
};
|
|
672
1077
|
}
|
|
673
1078
|
}
|
|
674
|
-
|
|
675
|
-
if (
|
|
1079
|
+
|
|
1080
|
+
if (
|
|
1081
|
+
extractedPk &&
|
|
1082
|
+
this.client.state.authorization
|
|
1083
|
+
) {
|
|
676
1084
|
try {
|
|
677
1085
|
const userInfo = await this.currentUser();
|
|
678
1086
|
const user = userInfo.user || userInfo;
|
|
679
1087
|
if (user && user.pk) {
|
|
680
|
-
this.client.state.cookieUserId =
|
|
681
|
-
|
|
1088
|
+
this.client.state.cookieUserId =
|
|
1089
|
+
String(user.pk);
|
|
1090
|
+
this.client.state._userId =
|
|
1091
|
+
String(user.pk);
|
|
682
1092
|
}
|
|
683
1093
|
return user;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
|
|
1094
|
+
} catch (e) {
|
|
1095
|
+
return {
|
|
1096
|
+
pk: parseInt(extractedPk),
|
|
1097
|
+
pk_id: extractedPk,
|
|
1098
|
+
};
|
|
687
1099
|
}
|
|
688
1100
|
}
|
|
689
1101
|
}
|
|
690
|
-
|
|
1102
|
+
|
|
691
1103
|
if (body && body.logged_in_user) {
|
|
692
1104
|
const lu = body.logged_in_user;
|
|
693
1105
|
if (lu.pk) {
|
|
694
1106
|
this.client.state.cookieUserId = String(lu.pk);
|
|
695
1107
|
this.client.state._userId = String(lu.pk);
|
|
696
1108
|
}
|
|
697
|
-
// Ensure authorization is saved if present in response headers
|
|
698
1109
|
this._extractAndSaveAuthorization(response);
|
|
699
1110
|
return lu;
|
|
700
1111
|
}
|
|
701
|
-
|
|
1112
|
+
|
|
702
1113
|
if (this.client.state.authorization) {
|
|
703
1114
|
try {
|
|
704
1115
|
const userInfo = await this.currentUser();
|
|
705
1116
|
const user = userInfo.user || userInfo;
|
|
706
1117
|
if (user && user.pk) {
|
|
707
|
-
this.client.state.cookieUserId =
|
|
1118
|
+
this.client.state.cookieUserId =
|
|
1119
|
+
String(user.pk);
|
|
708
1120
|
this.client.state._userId = String(user.pk);
|
|
709
1121
|
}
|
|
710
1122
|
return user;
|
|
711
|
-
}
|
|
712
|
-
catch (e) {
|
|
713
|
-
// fallback: return raw body
|
|
1123
|
+
} catch (e) {
|
|
714
1124
|
return body;
|
|
715
1125
|
}
|
|
716
1126
|
}
|
|
1127
|
+
|
|
717
1128
|
return body;
|
|
718
1129
|
});
|
|
719
1130
|
}
|
|
720
|
-
|
|
1131
|
+
|
|
1132
|
+
async twoFactorLogin(
|
|
1133
|
+
username,
|
|
1134
|
+
verificationCode,
|
|
1135
|
+
twoFactorIdentifier,
|
|
1136
|
+
verificationMethod = '1'
|
|
1137
|
+
) {
|
|
721
1138
|
return this.requestWithRetry(async () => {
|
|
722
1139
|
const response = await this.client.request.send({
|
|
723
1140
|
method: 'POST',
|
|
@@ -733,11 +1150,11 @@ class AccountRepository extends Repository {
|
|
|
733
1150
|
phone_id: this.client.state.phoneId,
|
|
734
1151
|
}),
|
|
735
1152
|
});
|
|
736
|
-
// Save authorization if present in two-factor response
|
|
737
1153
|
this._extractAndSaveAuthorization(response);
|
|
738
1154
|
return response.body;
|
|
739
1155
|
});
|
|
740
1156
|
}
|
|
1157
|
+
|
|
741
1158
|
async logout() {
|
|
742
1159
|
return this.requestWithRetry(async () => {
|
|
743
1160
|
const response = await this.client.request.send({
|
|
@@ -750,6 +1167,7 @@ class AccountRepository extends Repository {
|
|
|
750
1167
|
return response.body;
|
|
751
1168
|
});
|
|
752
1169
|
}
|
|
1170
|
+
|
|
753
1171
|
async currentUser() {
|
|
754
1172
|
return this.requestWithRetry(async () => {
|
|
755
1173
|
const response = await this.client.request.send({
|
|
@@ -760,10 +1178,19 @@ class AccountRepository extends Repository {
|
|
|
760
1178
|
return response.body;
|
|
761
1179
|
});
|
|
762
1180
|
}
|
|
1181
|
+
|
|
763
1182
|
async accountInfo() {
|
|
764
1183
|
return this.currentUser();
|
|
765
1184
|
}
|
|
766
|
-
|
|
1185
|
+
|
|
1186
|
+
async editProfile({
|
|
1187
|
+
externalUrl,
|
|
1188
|
+
phoneNumber,
|
|
1189
|
+
username,
|
|
1190
|
+
fullName,
|
|
1191
|
+
biography,
|
|
1192
|
+
email,
|
|
1193
|
+
} = {}) {
|
|
767
1194
|
return this.requestWithRetry(async () => {
|
|
768
1195
|
const current = await this.currentUser();
|
|
769
1196
|
const user = current.user || current;
|
|
@@ -772,26 +1199,48 @@ class AccountRepository extends Repository {
|
|
|
772
1199
|
url: '/api/v1/accounts/edit_profile/',
|
|
773
1200
|
form: this.client.request.sign({
|
|
774
1201
|
_uuid: this.client.state.uuid,
|
|
775
|
-
external_url:
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1202
|
+
external_url:
|
|
1203
|
+
externalUrl !== undefined
|
|
1204
|
+
? externalUrl
|
|
1205
|
+
: user.external_url || '',
|
|
1206
|
+
phone_number:
|
|
1207
|
+
phoneNumber !== undefined
|
|
1208
|
+
? phoneNumber
|
|
1209
|
+
: user.phone_number || '',
|
|
1210
|
+
username:
|
|
1211
|
+
username !== undefined
|
|
1212
|
+
? username
|
|
1213
|
+
: user.username,
|
|
1214
|
+
full_name:
|
|
1215
|
+
fullName !== undefined
|
|
1216
|
+
? fullName
|
|
1217
|
+
: user.full_name || '',
|
|
1218
|
+
biography:
|
|
1219
|
+
biography !== undefined
|
|
1220
|
+
? biography
|
|
1221
|
+
: user.biography || '',
|
|
1222
|
+
email:
|
|
1223
|
+
email !== undefined
|
|
1224
|
+
? email
|
|
1225
|
+
: user.email || '',
|
|
781
1226
|
}),
|
|
782
1227
|
});
|
|
783
1228
|
return response.body;
|
|
784
1229
|
});
|
|
785
1230
|
}
|
|
1231
|
+
|
|
786
1232
|
async setBiography(biography) {
|
|
787
1233
|
return this.editProfile({ biography });
|
|
788
1234
|
}
|
|
1235
|
+
|
|
789
1236
|
async setExternalUrl(url) {
|
|
790
1237
|
return this.editProfile({ externalUrl: url });
|
|
791
1238
|
}
|
|
1239
|
+
|
|
792
1240
|
async removeBioLinks() {
|
|
793
1241
|
return this.editProfile({ externalUrl: '' });
|
|
794
1242
|
}
|
|
1243
|
+
|
|
795
1244
|
async setGender(gender) {
|
|
796
1245
|
return this.requestWithRetry(async () => {
|
|
797
1246
|
const response = await this.client.request.send({
|
|
@@ -805,6 +1254,7 @@ class AccountRepository extends Repository {
|
|
|
805
1254
|
return response.body;
|
|
806
1255
|
});
|
|
807
1256
|
}
|
|
1257
|
+
|
|
808
1258
|
async setPrivate() {
|
|
809
1259
|
return this.requestWithRetry(async () => {
|
|
810
1260
|
const response = await this.client.request.send({
|
|
@@ -817,6 +1267,7 @@ class AccountRepository extends Repository {
|
|
|
817
1267
|
return response.body;
|
|
818
1268
|
});
|
|
819
1269
|
}
|
|
1270
|
+
|
|
820
1271
|
async setPublic() {
|
|
821
1272
|
return this.requestWithRetry(async () => {
|
|
822
1273
|
const response = await this.client.request.send({
|
|
@@ -829,6 +1280,7 @@ class AccountRepository extends Repository {
|
|
|
829
1280
|
return response.body;
|
|
830
1281
|
});
|
|
831
1282
|
}
|
|
1283
|
+
|
|
832
1284
|
async changePassword(oldPassword, newPassword) {
|
|
833
1285
|
const oldEnc = this.encryptPassword(oldPassword);
|
|
834
1286
|
const newEnc = this.encryptPassword(newPassword);
|
|
@@ -846,6 +1298,7 @@ class AccountRepository extends Repository {
|
|
|
846
1298
|
return response.body;
|
|
847
1299
|
});
|
|
848
1300
|
}
|
|
1301
|
+
|
|
849
1302
|
async sendConfirmEmail() {
|
|
850
1303
|
return this.requestWithRetry(async () => {
|
|
851
1304
|
const response = await this.client.request.send({
|
|
@@ -859,6 +1312,7 @@ class AccountRepository extends Repository {
|
|
|
859
1312
|
return response.body;
|
|
860
1313
|
});
|
|
861
1314
|
}
|
|
1315
|
+
|
|
862
1316
|
async sendConfirmPhoneNumber(phoneNumber) {
|
|
863
1317
|
return this.requestWithRetry(async () => {
|
|
864
1318
|
const response = await this.client.request.send({
|
|
@@ -872,6 +1326,7 @@ class AccountRepository extends Repository {
|
|
|
872
1326
|
return response.body;
|
|
873
1327
|
});
|
|
874
1328
|
}
|
|
1329
|
+
|
|
875
1330
|
async profilePictureChange(uploadId) {
|
|
876
1331
|
return this.requestWithRetry(async () => {
|
|
877
1332
|
const response = await this.client.request.send({
|
|
@@ -886,6 +1341,7 @@ class AccountRepository extends Repository {
|
|
|
886
1341
|
return response.body;
|
|
887
1342
|
});
|
|
888
1343
|
}
|
|
1344
|
+
|
|
889
1345
|
async profilePictureRemove() {
|
|
890
1346
|
return this.requestWithRetry(async () => {
|
|
891
1347
|
const response = await this.client.request.send({
|
|
@@ -898,6 +1354,7 @@ class AccountRepository extends Repository {
|
|
|
898
1354
|
return response.body;
|
|
899
1355
|
});
|
|
900
1356
|
}
|
|
1357
|
+
|
|
901
1358
|
async newsInbox() {
|
|
902
1359
|
return this.requestWithRetry(async () => {
|
|
903
1360
|
const response = await this.client.request.send({
|
|
@@ -907,6 +1364,7 @@ class AccountRepository extends Repository {
|
|
|
907
1364
|
return response.body;
|
|
908
1365
|
});
|
|
909
1366
|
}
|
|
1367
|
+
|
|
910
1368
|
async syncLoginExperiments() {
|
|
911
1369
|
return this.requestWithRetry(async () => {
|
|
912
1370
|
const response = await this.client.request.send({
|
|
@@ -915,20 +1373,23 @@ class AccountRepository extends Repository {
|
|
|
915
1373
|
form: this.client.request.sign({
|
|
916
1374
|
id: this.client.state.uuid,
|
|
917
1375
|
server_config_retrieval: '1',
|
|
918
|
-
experiments:
|
|
1376
|
+
experiments:
|
|
1377
|
+
this.client.state.constants
|
|
1378
|
+
.LOGIN_EXPERIMENTS,
|
|
919
1379
|
}),
|
|
920
1380
|
});
|
|
921
1381
|
return response.body;
|
|
922
1382
|
});
|
|
923
1383
|
}
|
|
1384
|
+
|
|
924
1385
|
async syncPostLoginExperiments() {
|
|
925
1386
|
let userId;
|
|
926
1387
|
try {
|
|
927
1388
|
userId = this.client.state.cookieUserId;
|
|
928
|
-
}
|
|
929
|
-
catch {
|
|
1389
|
+
} catch {
|
|
930
1390
|
userId = '0';
|
|
931
1391
|
}
|
|
1392
|
+
|
|
932
1393
|
return this.requestWithRetry(async () => {
|
|
933
1394
|
const response = await this.client.request.send({
|
|
934
1395
|
method: 'POST',
|
|
@@ -938,12 +1399,14 @@ class AccountRepository extends Repository {
|
|
|
938
1399
|
_uid: userId,
|
|
939
1400
|
_uuid: this.client.state.uuid,
|
|
940
1401
|
server_config_retrieval: '1',
|
|
941
|
-
experiments:
|
|
1402
|
+
experiments:
|
|
1403
|
+
this.client.state.constants.EXPERIMENTS,
|
|
942
1404
|
}),
|
|
943
1405
|
});
|
|
944
1406
|
return response.body;
|
|
945
1407
|
});
|
|
946
1408
|
}
|
|
1409
|
+
|
|
947
1410
|
async syncLauncher(preLogin = true) {
|
|
948
1411
|
const data = {
|
|
949
1412
|
id: this.client.state.uuid,
|
|
@@ -953,9 +1416,9 @@ class AccountRepository extends Repository {
|
|
|
953
1416
|
try {
|
|
954
1417
|
data._uid = this.client.state.cookieUserId;
|
|
955
1418
|
data._uuid = this.client.state.uuid;
|
|
956
|
-
}
|
|
957
|
-
catch { }
|
|
1419
|
+
} catch {}
|
|
958
1420
|
}
|
|
1421
|
+
|
|
959
1422
|
return this.requestWithRetry(async () => {
|
|
960
1423
|
const response = await this.client.request.send({
|
|
961
1424
|
method: 'POST',
|
|
@@ -965,6 +1428,7 @@ class AccountRepository extends Repository {
|
|
|
965
1428
|
return response.body;
|
|
966
1429
|
});
|
|
967
1430
|
}
|
|
1431
|
+
|
|
968
1432
|
async syncDeviceFeatures() {
|
|
969
1433
|
return this.requestWithRetry(async () => {
|
|
970
1434
|
const response = await this.client.request.send({
|
|
@@ -978,6 +1442,7 @@ class AccountRepository extends Repository {
|
|
|
978
1442
|
return response.body;
|
|
979
1443
|
});
|
|
980
1444
|
}
|
|
1445
|
+
|
|
981
1446
|
async getPrefillCandidates() {
|
|
982
1447
|
return this.requestWithRetry(async () => {
|
|
983
1448
|
const response = await this.client.request.send({
|
|
@@ -993,6 +1458,7 @@ class AccountRepository extends Repository {
|
|
|
993
1458
|
return response.body;
|
|
994
1459
|
});
|
|
995
1460
|
}
|
|
1461
|
+
|
|
996
1462
|
async contactPointPrefill(usage = 'prefill') {
|
|
997
1463
|
return this.requestWithRetry(async () => {
|
|
998
1464
|
const response = await this.client.request.send({
|
|
@@ -1006,6 +1472,7 @@ class AccountRepository extends Repository {
|
|
|
1006
1472
|
return response.body;
|
|
1007
1473
|
});
|
|
1008
1474
|
}
|
|
1475
|
+
|
|
1009
1476
|
async getZrToken(params = {}) {
|
|
1010
1477
|
return this.requestWithRetry(async () => {
|
|
1011
1478
|
const response = await this.client.request.send({
|
|
@@ -1022,6 +1489,7 @@ class AccountRepository extends Repository {
|
|
|
1022
1489
|
return response.body;
|
|
1023
1490
|
});
|
|
1024
1491
|
}
|
|
1492
|
+
|
|
1025
1493
|
async getConsentSignupConfig() {
|
|
1026
1494
|
return this.requestWithRetry(async () => {
|
|
1027
1495
|
const response = await this.client.request.send({
|
|
@@ -1035,6 +1503,7 @@ class AccountRepository extends Repository {
|
|
|
1035
1503
|
return response.body;
|
|
1036
1504
|
});
|
|
1037
1505
|
}
|
|
1506
|
+
|
|
1038
1507
|
async sendRecoveryFlowEmail(query) {
|
|
1039
1508
|
return this.requestWithRetry(async () => {
|
|
1040
1509
|
const response = await this.client.request.send({
|
|
@@ -1050,6 +1519,7 @@ class AccountRepository extends Repository {
|
|
|
1050
1519
|
return response.body;
|
|
1051
1520
|
});
|
|
1052
1521
|
}
|
|
1522
|
+
|
|
1053
1523
|
async sendRecoveryFlowSms(query) {
|
|
1054
1524
|
return this.requestWithRetry(async () => {
|
|
1055
1525
|
const response = await this.client.request.send({
|
|
@@ -1065,40 +1535,64 @@ class AccountRepository extends Repository {
|
|
|
1065
1535
|
return response.body;
|
|
1066
1536
|
});
|
|
1067
1537
|
}
|
|
1538
|
+
|
|
1068
1539
|
static generateAttestParams(state) {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
namedCurve: 'prime256v1',
|
|
1084
|
-
});
|
|
1085
|
-
// Sign the challenge nonce with the private key (simulating TEE signing).
|
|
1086
|
-
const signedData = crypto.sign(null, Buffer.from(challengeNonce), privateKey);
|
|
1540
|
+
const challengeNonce = crypto
|
|
1541
|
+
.randomBytes(24)
|
|
1542
|
+
.toString('base64url');
|
|
1543
|
+
const { privateKey, publicKey } = crypto.generateKeyPairSync(
|
|
1544
|
+
'ec',
|
|
1545
|
+
{
|
|
1546
|
+
namedCurve: 'prime256v1',
|
|
1547
|
+
}
|
|
1548
|
+
);
|
|
1549
|
+
const signedData = crypto.sign(
|
|
1550
|
+
null,
|
|
1551
|
+
Buffer.from(challengeNonce),
|
|
1552
|
+
privateKey
|
|
1553
|
+
);
|
|
1087
1554
|
const signedNonce = signedData.toString('base64');
|
|
1088
|
-
const publicKeyDer = publicKey.export({
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1555
|
+
const publicKeyDer = publicKey.export({
|
|
1556
|
+
type: 'spki',
|
|
1557
|
+
format: 'der',
|
|
1558
|
+
});
|
|
1559
|
+
const keyHash = crypto
|
|
1560
|
+
.createHash('sha256')
|
|
1561
|
+
.update(publicKeyDer)
|
|
1562
|
+
.digest('hex');
|
|
1563
|
+
|
|
1564
|
+
const leafCertPem =
|
|
1565
|
+
AccountRepository._generateSelfSignedCert(
|
|
1566
|
+
privateKey,
|
|
1567
|
+
publicKey,
|
|
1568
|
+
'Android Keystore Key'
|
|
1569
|
+
);
|
|
1570
|
+
const intermediate1Pem =
|
|
1571
|
+
AccountRepository._generateSelfSignedCert(
|
|
1572
|
+
privateKey,
|
|
1573
|
+
publicKey,
|
|
1574
|
+
'Android Keystore Key Attestation'
|
|
1575
|
+
);
|
|
1576
|
+
const intermediate2Pem =
|
|
1577
|
+
AccountRepository._generateSelfSignedCert(
|
|
1578
|
+
privateKey,
|
|
1579
|
+
publicKey,
|
|
1580
|
+
'Android Hardware Keystore'
|
|
1581
|
+
);
|
|
1582
|
+
const rootCertPem =
|
|
1583
|
+
AccountRepository._generateSelfSignedCert(
|
|
1584
|
+
privateKey,
|
|
1585
|
+
publicKey,
|
|
1586
|
+
'Android Keystore Root'
|
|
1587
|
+
);
|
|
1588
|
+
|
|
1096
1589
|
const certificateChain = [
|
|
1097
1590
|
leafCertPem,
|
|
1098
1591
|
intermediate1Pem,
|
|
1099
1592
|
intermediate2Pem,
|
|
1100
1593
|
rootCertPem,
|
|
1101
1594
|
].join('\n');
|
|
1595
|
+
|
|
1102
1596
|
return {
|
|
1103
1597
|
attestation: [
|
|
1104
1598
|
{
|
|
@@ -1113,12 +1607,20 @@ class AccountRepository extends Repository {
|
|
|
1113
1607
|
],
|
|
1114
1608
|
};
|
|
1115
1609
|
}
|
|
1610
|
+
|
|
1116
1611
|
static _generateSelfSignedCert(privateKey, publicKey, cn) {
|
|
1117
|
-
const publicKeyDer = publicKey.export({
|
|
1612
|
+
const publicKeyDer = publicKey.export({
|
|
1613
|
+
type: 'spki',
|
|
1614
|
+
format: 'der',
|
|
1615
|
+
});
|
|
1118
1616
|
const serialNumber = crypto.randomBytes(8);
|
|
1119
1617
|
const now = new Date();
|
|
1120
|
-
const notBefore = new Date(
|
|
1121
|
-
|
|
1618
|
+
const notBefore = new Date(
|
|
1619
|
+
now.getTime() - 365 * 24 * 60 * 60 * 1000
|
|
1620
|
+
);
|
|
1621
|
+
const notAfter = new Date(
|
|
1622
|
+
now.getTime() + 10 * 365 * 24 * 60 * 60 * 1000
|
|
1623
|
+
);
|
|
1122
1624
|
const cnBytes = Buffer.from(cn, 'utf8');
|
|
1123
1625
|
const cnSeq = Buffer.concat([
|
|
1124
1626
|
Buffer.from([0x30, cnBytes.length + 13]),
|
|
@@ -1128,39 +1630,75 @@ class AccountRepository extends Repository {
|
|
|
1128
1630
|
Buffer.from([0x0c, cnBytes.length]),
|
|
1129
1631
|
cnBytes,
|
|
1130
1632
|
]);
|
|
1633
|
+
|
|
1131
1634
|
function encodeTime(date) {
|
|
1132
|
-
const str =
|
|
1133
|
-
|
|
1635
|
+
const str =
|
|
1636
|
+
date
|
|
1637
|
+
.toISOString()
|
|
1638
|
+
.replace(/[-:T]/g, '')
|
|
1639
|
+
.slice(2, 14) + 'Z';
|
|
1640
|
+
return Buffer.concat([
|
|
1641
|
+
Buffer.from([0x17, str.length]),
|
|
1642
|
+
Buffer.from(str),
|
|
1643
|
+
]);
|
|
1134
1644
|
}
|
|
1645
|
+
|
|
1135
1646
|
const validityBuf = Buffer.concat([
|
|
1136
1647
|
encodeTime(notBefore),
|
|
1137
1648
|
encodeTime(notAfter),
|
|
1138
1649
|
]);
|
|
1139
|
-
const validity = Buffer.concat([
|
|
1650
|
+
const validity = Buffer.concat([
|
|
1651
|
+
Buffer.from([0x30, validityBuf.length]),
|
|
1652
|
+
validityBuf,
|
|
1653
|
+
]);
|
|
1654
|
+
|
|
1140
1655
|
const tbs = Buffer.concat([
|
|
1141
1656
|
Buffer.from([0xa0, 0x03, 0x02, 0x01, 0x02]),
|
|
1142
|
-
Buffer.from([0x02, serialNumber.length]),
|
|
1143
|
-
|
|
1657
|
+
Buffer.from([0x02, serialNumber.length]),
|
|
1658
|
+
serialNumber,
|
|
1659
|
+
Buffer.from([
|
|
1660
|
+
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
|
|
1661
|
+
0x04, 0x03, 0x02,
|
|
1662
|
+
]),
|
|
1144
1663
|
cnSeq,
|
|
1145
1664
|
validity,
|
|
1146
1665
|
cnSeq,
|
|
1147
1666
|
publicKeyDer,
|
|
1148
1667
|
]);
|
|
1149
|
-
|
|
1668
|
+
|
|
1669
|
+
const tbsSeq = Buffer.concat([
|
|
1670
|
+
Buffer.from([0x30, 0x82]),
|
|
1671
|
+
Buffer.alloc(2),
|
|
1672
|
+
tbs,
|
|
1673
|
+
]);
|
|
1150
1674
|
tbsSeq.writeUInt16BE(tbs.length, 2);
|
|
1675
|
+
|
|
1151
1676
|
const signature = crypto.sign(null, tbsSeq, privateKey);
|
|
1152
1677
|
const sigBitString = Buffer.concat([
|
|
1153
1678
|
Buffer.from([0x03, signature.length + 1, 0x00]),
|
|
1154
1679
|
signature,
|
|
1155
1680
|
]);
|
|
1156
|
-
const algId = Buffer.from([
|
|
1681
|
+
const algId = Buffer.from([
|
|
1682
|
+
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
|
|
1683
|
+
0x04, 0x03, 0x02,
|
|
1684
|
+
]);
|
|
1157
1685
|
const certBody = Buffer.concat([tbsSeq, algId, sigBitString]);
|
|
1158
|
-
const cert = Buffer.concat([
|
|
1686
|
+
const cert = Buffer.concat([
|
|
1687
|
+
Buffer.from([0x30, 0x82]),
|
|
1688
|
+
Buffer.alloc(2),
|
|
1689
|
+
certBody,
|
|
1690
|
+
]);
|
|
1159
1691
|
cert.writeUInt16BE(certBody.length, 2);
|
|
1692
|
+
|
|
1160
1693
|
const b64 = cert.toString('base64');
|
|
1161
1694
|
const lines = b64.match(/.{1,76}/g) || [b64];
|
|
1162
|
-
return
|
|
1695
|
+
return (
|
|
1696
|
+
'-----BEGIN CERTIFICATE-----\n' +
|
|
1697
|
+
lines.join('\n') +
|
|
1698
|
+
'\n-----END CERTIFICATE-----'
|
|
1699
|
+
);
|
|
1163
1700
|
}
|
|
1701
|
+
|
|
1164
1702
|
static createJazoest(input) {
|
|
1165
1703
|
const buf = Buffer.from(input || '', 'ascii');
|
|
1166
1704
|
let sum = 0;
|
|
@@ -1169,45 +1707,70 @@ class AccountRepository extends Repository {
|
|
|
1169
1707
|
}
|
|
1170
1708
|
return `2${sum}`;
|
|
1171
1709
|
}
|
|
1710
|
+
|
|
1172
1711
|
encryptPassword(password) {
|
|
1173
1712
|
if (!this.client.state.passwordEncryptionPubKey) {
|
|
1174
|
-
return {
|
|
1713
|
+
return {
|
|
1714
|
+
time: Math.floor(Date.now() / 1000).toString(),
|
|
1715
|
+
encrypted: password,
|
|
1716
|
+
};
|
|
1175
1717
|
}
|
|
1176
1718
|
const randKey = crypto.randomBytes(32);
|
|
1177
1719
|
const iv = crypto.randomBytes(12);
|
|
1178
|
-
const rsaEncrypted = crypto.publicEncrypt(
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1720
|
+
const rsaEncrypted = crypto.publicEncrypt(
|
|
1721
|
+
{
|
|
1722
|
+
key: Buffer.from(
|
|
1723
|
+
this.client.state.passwordEncryptionPubKey,
|
|
1724
|
+
'base64'
|
|
1725
|
+
).toString(),
|
|
1726
|
+
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
1727
|
+
},
|
|
1728
|
+
randKey
|
|
1729
|
+
);
|
|
1730
|
+
const cipher = crypto.createCipheriv(
|
|
1731
|
+
'aes-256-gcm',
|
|
1732
|
+
randKey,
|
|
1733
|
+
iv
|
|
1734
|
+
);
|
|
1183
1735
|
const time = Math.floor(Date.now() / 1000).toString();
|
|
1184
1736
|
cipher.setAAD(Buffer.from(time));
|
|
1185
|
-
const aesEncrypted = Buffer.concat([
|
|
1737
|
+
const aesEncrypted = Buffer.concat([
|
|
1738
|
+
cipher.update(password, 'utf8'),
|
|
1739
|
+
cipher.final(),
|
|
1740
|
+
]);
|
|
1186
1741
|
const sizeBuffer = Buffer.alloc(2, 0);
|
|
1187
1742
|
sizeBuffer.writeInt16LE(rsaEncrypted.byteLength, 0);
|
|
1188
1743
|
const authTag = cipher.getAuthTag();
|
|
1189
1744
|
return {
|
|
1190
1745
|
time,
|
|
1191
1746
|
encrypted: Buffer.concat([
|
|
1192
|
-
Buffer.from([
|
|
1747
|
+
Buffer.from([
|
|
1748
|
+
1,
|
|
1749
|
+
this.client.state.passwordEncryptionKeyId || 0,
|
|
1750
|
+
]),
|
|
1193
1751
|
iv,
|
|
1194
1752
|
sizeBuffer,
|
|
1195
1753
|
rsaEncrypted,
|
|
1196
1754
|
authTag,
|
|
1197
|
-
aesEncrypted
|
|
1198
|
-
]).toString('base64')
|
|
1755
|
+
aesEncrypted,
|
|
1756
|
+
]).toString('base64'),
|
|
1199
1757
|
};
|
|
1200
1758
|
}
|
|
1759
|
+
|
|
1201
1760
|
async passwordPublicKeys() {
|
|
1202
1761
|
const response = await this.client.request.send({
|
|
1203
1762
|
method: 'GET',
|
|
1204
1763
|
url: '/api/v1/qe/sync/',
|
|
1205
1764
|
});
|
|
1206
1765
|
const headers = response.headers || {};
|
|
1207
|
-
const keyId = parseInt(
|
|
1208
|
-
|
|
1766
|
+
const keyId = parseInt(
|
|
1767
|
+
headers['ig-set-password-encryption-key-id'] || '0'
|
|
1768
|
+
);
|
|
1769
|
+
const pubKey =
|
|
1770
|
+
headers['ig-set-password-encryption-pub-key'] || '';
|
|
1209
1771
|
return { keyId, pubKey };
|
|
1210
1772
|
}
|
|
1773
|
+
|
|
1211
1774
|
async setPresenceDisabled(disabled = true) {
|
|
1212
1775
|
return this.requestWithRetry(async () => {
|
|
1213
1776
|
const response = await this.client.request.send({
|
|
@@ -1221,6 +1784,7 @@ class AccountRepository extends Repository {
|
|
|
1221
1784
|
return response.body;
|
|
1222
1785
|
});
|
|
1223
1786
|
}
|
|
1787
|
+
|
|
1224
1788
|
async getCommentFilter() {
|
|
1225
1789
|
return this.requestWithRetry(async () => {
|
|
1226
1790
|
const response = await this.client.request.send({
|
|
@@ -1230,6 +1794,7 @@ class AccountRepository extends Repository {
|
|
|
1230
1794
|
return response.body;
|
|
1231
1795
|
});
|
|
1232
1796
|
}
|
|
1797
|
+
|
|
1233
1798
|
async setCommentFilter(configValue = 0) {
|
|
1234
1799
|
return this.requestWithRetry(async () => {
|
|
1235
1800
|
const response = await this.client.request.send({
|
|
@@ -1243,6 +1808,7 @@ class AccountRepository extends Repository {
|
|
|
1243
1808
|
return response.body;
|
|
1244
1809
|
});
|
|
1245
1810
|
}
|
|
1811
|
+
|
|
1246
1812
|
async pushPreferences(preferences = 'default') {
|
|
1247
1813
|
return this.requestWithRetry(async () => {
|
|
1248
1814
|
const response = await this.client.request.send({
|
|
@@ -1262,4 +1828,5 @@ class AccountRepository extends Repository {
|
|
|
1262
1828
|
});
|
|
1263
1829
|
}
|
|
1264
1830
|
}
|
|
1831
|
+
|
|
1265
1832
|
module.exports = AccountRepository;
|