nodejs-insta-private-api-mqt 1.3.77 → 1.3.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/repositories/account.repository.js +831 -799
- package/package.json +1 -1
|
@@ -1,806 +1,838 @@
|
|
|
1
|
+
// AccountRepository.js
|
|
2
|
+
// Complete file with improved certificate generation using node-forge
|
|
3
|
+
// Ready to copy and use. Requires: npm install node-forge uuid
|
|
4
|
+
|
|
1
5
|
const Repository = require('../core/repository');
|
|
2
6
|
const crypto = require('crypto');
|
|
7
|
+
const forge = require('node-forge');
|
|
8
|
+
const { v4: uuidv4 } = require('uuid');
|
|
3
9
|
|
|
4
10
|
class AccountRepository extends Repository {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
11
|
+
constructor(client) {
|
|
12
|
+
super(client);
|
|
13
|
+
this.maxRetries = 3;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async requestWithRetry(requestFn, retries = 0) {
|
|
17
|
+
try {
|
|
18
|
+
const result = await requestFn();
|
|
19
|
+
return result;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const shouldRetry =
|
|
22
|
+
(error.data?.error_type === 'server_error' ||
|
|
23
|
+
error.data?.error_type === 'rate_limited') &&
|
|
24
|
+
retries < this.maxRetries;
|
|
25
|
+
if (shouldRetry) {
|
|
26
|
+
const delay = 1000 * (retries + 1);
|
|
27
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
28
|
+
return this.requestWithRetry(requestFn, retries + 1);
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async login(credentialsOrUsername, passwordArg) {
|
|
35
|
+
let username, password;
|
|
36
|
+
if (typeof credentialsOrUsername === 'object' && credentialsOrUsername !== null) {
|
|
37
|
+
username = credentialsOrUsername.username;
|
|
38
|
+
password = credentialsOrUsername.password;
|
|
39
|
+
} else {
|
|
40
|
+
username = credentialsOrUsername;
|
|
41
|
+
password = passwordArg;
|
|
42
|
+
}
|
|
43
|
+
if (!username || !password) {
|
|
44
|
+
throw new Error('Username and password are required');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!this.client.state.passwordEncryptionPubKey) {
|
|
48
|
+
await this.syncLoginExperiments();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const { encrypted, time } = this.encryptPassword(password);
|
|
52
|
+
|
|
53
|
+
return this.requestWithRetry(async () => {
|
|
54
|
+
const aacInitTimestamp = Math.floor(Date.now() / 1000) - Math.floor(Math.random() * 50);
|
|
55
|
+
const aacjid = crypto.randomUUID ? crypto.randomUUID() : uuidv4();
|
|
56
|
+
const aaccs = crypto.randomBytes(32).toString('base64url');
|
|
57
|
+
|
|
58
|
+
const clientInputParams = {
|
|
59
|
+
aac: JSON.stringify({
|
|
60
|
+
aac_init_timestamp: aacInitTimestamp,
|
|
61
|
+
aacjid: aacjid,
|
|
62
|
+
aaccs: aaccs,
|
|
63
|
+
}),
|
|
64
|
+
sim_phones: [],
|
|
65
|
+
aymh_accounts: [],
|
|
66
|
+
network_bssid: null,
|
|
67
|
+
secure_family_device_id: '',
|
|
68
|
+
has_granted_read_contacts_permissions: 0,
|
|
69
|
+
auth_secure_device_id: '',
|
|
70
|
+
has_whatsapp_installed: 1,
|
|
71
|
+
password: `#PWD_INSTAGRAM:4:${time}:${encrypted}`,
|
|
72
|
+
sso_token_map_json_string: '{}',
|
|
73
|
+
block_store_machine_id: '',
|
|
74
|
+
ig_vetted_device_nonces: '{}',
|
|
75
|
+
contact_point: username,
|
|
76
|
+
machine_id: this.client.state.mid || '',
|
|
77
|
+
login_attempt_count: '0',
|
|
78
|
+
reg_flow_taken: 'phone',
|
|
79
|
+
device_id: this.client.state.uuid,
|
|
80
|
+
phone_id: this.client.state.phoneId,
|
|
81
|
+
family_device_id: this.client.state.phoneId,
|
|
82
|
+
encryption_enabled: '1',
|
|
83
|
+
has_dbl_tap_login: 0,
|
|
84
|
+
jazoest: AccountRepository.createJazoest(this.client.state.phoneId || ''),
|
|
85
|
+
openid_tokens: '{}',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const serverParams = {
|
|
89
|
+
credential_type: 'password',
|
|
90
|
+
device_id: this.client.state.uuid,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const paramsJson = JSON.stringify({
|
|
94
|
+
client_input_params: clientInputParams,
|
|
95
|
+
server_params: serverParams,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Generate attest params using node-forge to create more standard X.509 certs
|
|
99
|
+
const attestParams = AccountRepository.generateAttestParams(this.client.state);
|
|
100
|
+
|
|
101
|
+
// Build headers using values from payload or fallbacks from client.state
|
|
102
|
+
const bloksHeaders = {
|
|
103
|
+
'accept-language': 'ro-RO, en-US',
|
|
104
|
+
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
105
|
+
'ig-intended-user-id': '0',
|
|
106
|
+
'priority': 'u=3',
|
|
107
|
+
'x-bloks-is-layout-rtl': 'false',
|
|
108
|
+
'x-bloks-prism-ax-base-colors-enabled': 'false',
|
|
109
|
+
'x-bloks-prism-button-version': 'CONTROL',
|
|
110
|
+
'x-bloks-prism-colors-enabled': 'true',
|
|
111
|
+
'x-bloks-prism-font-enabled': 'false',
|
|
112
|
+
'x-bloks-prism-indigo-link-version': '0',
|
|
113
|
+
'x-bloks-version-id': this.client.state.bloksVersionId || '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b',
|
|
114
|
+
'x-fb-client-ip': 'True',
|
|
115
|
+
'x-fb-connection-type': this.client.state.connectionType || 'MOBILE.LTE',
|
|
116
|
+
'x-fb-friendly-name': 'IgApi: bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
|
|
117
|
+
'x-fb-network-properties': this.client.state.fbNetworkProperties || 'VPN;Metered;Validated;LocalAddrs=/10.0.0.2,;',
|
|
118
|
+
'x-fb-request-analytics-tags': JSON.stringify({
|
|
119
|
+
network_tags: {
|
|
120
|
+
product: this.client.state.fbAnalyticsApplicationId || '567067343352427',
|
|
121
|
+
purpose: 'fetch',
|
|
122
|
+
surface: 'undefined',
|
|
123
|
+
request_category: 'api',
|
|
124
|
+
retry_attempt: '0',
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
'x-fb-server-cluster': 'True',
|
|
128
|
+
'x-ig-android-id': this.client.state.androidId || `android-${crypto.randomBytes(8).toString('hex')}`,
|
|
129
|
+
'x-ig-app-id': this.client.state.fbAnalyticsApplicationId || '567067343352427',
|
|
130
|
+
'x-ig-app-locale': this.client.state.locale || 'ro_RO',
|
|
131
|
+
'x-ig-attest-params': JSON.stringify(attestParams),
|
|
132
|
+
'x-ig-bandwidth-speed-kbps': String(this.client.state.bandwidthSpeedKbps || '2357.000'),
|
|
133
|
+
'x-ig-bandwidth-totalbytes-b': String(this.client.state.bandwidthTotalBytes || '0'),
|
|
134
|
+
'x-ig-bandwidth-totaltime-ms': String(this.client.state.bandwidthTotalTimeMs || '0'),
|
|
135
|
+
'x-ig-client-endpoint': 'com.bloks.www.caa.login.aymh_single_profile_screen_entry',
|
|
136
|
+
'x-ig-capabilities': this.client.state.capabilities || '3brTv10=',
|
|
137
|
+
'x-ig-connection-type': this.client.state.connectionType || 'MOBILE(LTE)',
|
|
138
|
+
'x-ig-device-id': this.client.state.deviceId || (this.client.state.uuid || ''),
|
|
139
|
+
'x-ig-device-locale': this.client.state.locale || 'ro_RO',
|
|
140
|
+
'x-ig-family-device-id': this.client.state.familyDeviceId || this.client.state.phoneId || '',
|
|
141
|
+
'x-ig-mapped-locale': this.client.state.locale || 'ro_RO',
|
|
142
|
+
'x-ig-nav-chain': this.client.state.navChain || '',
|
|
143
|
+
'x-ig-timezone-offset': String(this.client.state.timezoneOffset || (new Date().getTimezoneOffset() * -60)),
|
|
144
|
+
'x-ig-www-claim': this.client.state.igWWWClaim || '0',
|
|
145
|
+
'x-mid': this.client.state.mid || '',
|
|
146
|
+
'x-pigeon-rawclienttime': String(this.client.state.pigeonRawClientTime || Math.floor(Date.now() / 1000)),
|
|
147
|
+
'x-pigeon-session-id': this.client.state.pigeonSessionId || `UFS-${uuidv4()}`,
|
|
148
|
+
'x-tigon-is-retry': 'False',
|
|
149
|
+
'accept-encoding': 'gzip, deflate',
|
|
150
|
+
'user-agent': this.client.state.userAgent || 'Instagram 371.0.0.0.23 Android (30/11; 320dpi; 720x1449; Xiaomi/Redmi; M2006C3MNG; angelican; mt6765; ro_RO; 703217507)',
|
|
151
|
+
'x-fb-conn-uuid-client': this.client.state.connUuidClient || crypto.randomBytes(16).toString('hex'),
|
|
152
|
+
'x-fb-http-engine': this.client.state.fbHttpEngine || 'Liger',
|
|
153
|
+
'x-fb-rmd': this.client.state.fbRmd || 'state=URL_ELIGIBLE',
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Remove undefined/null headers
|
|
157
|
+
Object.keys(bloksHeaders).forEach(k => {
|
|
158
|
+
if (bloksHeaders[k] === undefined || bloksHeaders[k] === null) delete bloksHeaders[k];
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const response = await this.client.request.send({
|
|
162
|
+
method: 'POST',
|
|
163
|
+
url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
|
|
164
|
+
form: { params: paramsJson },
|
|
165
|
+
headers: bloksHeaders,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const body = response.body;
|
|
169
|
+
|
|
170
|
+
if (body.two_factor_required) {
|
|
171
|
+
const err = new Error('Two factor authentication required');
|
|
172
|
+
err.name = 'IgLoginTwoFactorRequiredError';
|
|
173
|
+
err.twoFactorInfo = body.two_factor_info;
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
if (body.error_type === 'bad_password') {
|
|
177
|
+
const err = new Error('Bad password');
|
|
178
|
+
err.name = 'IgLoginBadPasswordError';
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
if (body.error_type === 'invalid_user') {
|
|
182
|
+
const err = new Error('Invalid user');
|
|
183
|
+
err.name = 'IgLoginInvalidUserError';
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
if (body.message === 'challenge_required') {
|
|
187
|
+
const err = new Error('Challenge required');
|
|
188
|
+
err.name = 'IgCheckpointError';
|
|
189
|
+
err.challengeInfo = body.challenge;
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (body.layout) {
|
|
194
|
+
const layoutStr = typeof body.layout === 'string' ? body.layout : JSON.stringify(body.layout);
|
|
195
|
+
|
|
196
|
+
if (layoutStr.includes('two_factor_required') || layoutStr.includes('"two_factor_info"')) {
|
|
197
|
+
let twoFactorInfo = null;
|
|
198
|
+
try {
|
|
199
|
+
const match = layoutStr.match(/"two_factor_info"\s*:\s*(\{[^}]+\})/);
|
|
200
|
+
if (match) twoFactorInfo = JSON.parse(match[1]);
|
|
201
|
+
} catch (e) {}
|
|
202
|
+
const err = new Error('Two factor authentication required');
|
|
203
|
+
err.name = 'IgLoginTwoFactorRequiredError';
|
|
204
|
+
err.twoFactorInfo = twoFactorInfo;
|
|
205
|
+
throw err;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (layoutStr.includes('bad_password') || layoutStr.includes('incorrect_password')) {
|
|
209
|
+
const err = new Error('Bad password');
|
|
210
|
+
err.name = 'IgLoginBadPasswordError';
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (layoutStr.includes('invalid_user') || layoutStr.includes('user_not_found')) {
|
|
215
|
+
const err = new Error('Invalid user');
|
|
216
|
+
err.name = 'IgLoginInvalidUserError';
|
|
217
|
+
throw err;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (layoutStr.includes('challenge_required')) {
|
|
221
|
+
const err = new Error('Challenge required');
|
|
222
|
+
err.name = 'IgCheckpointError';
|
|
223
|
+
err.challengeInfo = body.challenge || null;
|
|
224
|
+
throw err;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const bearerMatch = layoutStr.match(/Bearer IGT:2:[A-Za-z0-9+\/=]+/);
|
|
228
|
+
if (bearerMatch) {
|
|
229
|
+
this.client.state.authorization = bearerMatch[0];
|
|
230
|
+
if (typeof this.client.state.updateAuthorization === 'function') {
|
|
231
|
+
this.client.state.updateAuthorization();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const pkMatch = layoutStr.match(/\\"pk\\":(\d+)/);
|
|
236
|
+
if (pkMatch) {
|
|
237
|
+
this.client.state.cookieUserId = pkMatch[1];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const wwwClaimMatch = layoutStr.match(/IG-Set-WWW-Claim[\\"\s:]+([a-f0-9]+)/i);
|
|
241
|
+
if (wwwClaimMatch) {
|
|
242
|
+
this.client.state.igWWWClaim = wwwClaimMatch[1];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const encKeyIdMatch = layoutStr.match(/IG-Set-Password-Encryption-Key-Id[\\"\s:]+(\d+)/i);
|
|
246
|
+
if (encKeyIdMatch) {
|
|
247
|
+
this.client.state.passwordEncryptionKeyId = parseInt(encKeyIdMatch[1]);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const encPubKeyMatch = layoutStr.match(/IG-Set-Password-Encryption-Pub-Key[\\"\s:]+([A-Za-z0-9+\/=]+)/i);
|
|
251
|
+
if (encPubKeyMatch) {
|
|
252
|
+
this.client.state.passwordEncryptionPubKey = encPubKeyMatch[1];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const loginResponseMatch = layoutStr.match(/\\"logged_in_user\\":\{[^}]*\\"pk\\":(\d+)[^}]*\\"username\\":\\"([^"\\]+)\\"/);
|
|
256
|
+
if (loginResponseMatch) {
|
|
257
|
+
return {
|
|
258
|
+
pk: parseInt(loginResponseMatch[1]),
|
|
259
|
+
pk_id: loginResponseMatch[1],
|
|
260
|
+
username: loginResponseMatch[2],
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (body.logged_in_user) {
|
|
266
|
+
return body.logged_in_user;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const userInfo = await this.currentUser();
|
|
271
|
+
return userInfo.user || userInfo;
|
|
272
|
+
} catch (e) {
|
|
273
|
+
return body;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async twoFactorLogin(username, verificationCode, twoFactorIdentifier, verificationMethod = '1') {
|
|
280
|
+
return this.requestWithRetry(async () => {
|
|
281
|
+
const response = await this.client.request.send({
|
|
282
|
+
method: 'POST',
|
|
283
|
+
url: '/api/v1/accounts/two_factor_login/',
|
|
284
|
+
form: this.client.request.sign({
|
|
285
|
+
username,
|
|
286
|
+
verification_code: verificationCode,
|
|
287
|
+
two_factor_identifier: twoFactorIdentifier,
|
|
288
|
+
verification_method: verificationMethod,
|
|
289
|
+
trust_this_device: '1',
|
|
290
|
+
guid: this.client.state.uuid,
|
|
291
|
+
device_id: this.client.state.deviceId,
|
|
292
|
+
phone_id: this.client.state.phoneId,
|
|
293
|
+
}),
|
|
294
|
+
});
|
|
295
|
+
return response.body;
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async logout() {
|
|
300
|
+
return this.requestWithRetry(async () => {
|
|
301
|
+
const response = await this.client.request.send({
|
|
302
|
+
method: 'POST',
|
|
303
|
+
url: '/api/v1/accounts/logout/',
|
|
304
|
+
form: this.client.request.sign({
|
|
305
|
+
_uuid: this.client.state.uuid,
|
|
306
|
+
}),
|
|
307
|
+
});
|
|
308
|
+
return response.body;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async currentUser() {
|
|
313
|
+
return this.requestWithRetry(async () => {
|
|
314
|
+
const response = await this.client.request.send({
|
|
315
|
+
method: 'GET',
|
|
316
|
+
url: '/api/v1/accounts/current_user/',
|
|
317
|
+
qs: { edit: true },
|
|
318
|
+
});
|
|
319
|
+
return response.body;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async accountInfo() {
|
|
324
|
+
return this.currentUser();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async editProfile({ externalUrl, phoneNumber, username, fullName, biography, email } = {}) {
|
|
328
|
+
return this.requestWithRetry(async () => {
|
|
329
|
+
const current = await this.currentUser();
|
|
330
|
+
const user = current.user || current;
|
|
331
|
+
const response = await this.client.request.send({
|
|
332
|
+
method: 'POST',
|
|
333
|
+
url: '/api/v1/accounts/edit_profile/',
|
|
334
|
+
form: this.client.request.sign({
|
|
335
|
+
_uuid: this.client.state.uuid,
|
|
336
|
+
external_url: externalUrl !== undefined ? externalUrl : (user.external_url || ''),
|
|
337
|
+
phone_number: phoneNumber !== undefined ? phoneNumber : (user.phone_number || ''),
|
|
338
|
+
username: username !== undefined ? username : user.username,
|
|
339
|
+
full_name: fullName !== undefined ? fullName : (user.full_name || ''),
|
|
340
|
+
biography: biography !== undefined ? biography : (user.biography || ''),
|
|
341
|
+
email: email !== undefined ? email : (user.email || ''),
|
|
342
|
+
}),
|
|
343
|
+
});
|
|
344
|
+
return response.body;
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async setBiography(biography) {
|
|
349
|
+
return this.editProfile({ biography });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async setExternalUrl(url) {
|
|
353
|
+
return this.editProfile({ externalUrl: url });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async removeBioLinks() {
|
|
357
|
+
return this.editProfile({ externalUrl: '' });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async setGender(gender) {
|
|
361
|
+
return this.requestWithRetry(async () => {
|
|
362
|
+
const response = await this.client.request.send({
|
|
363
|
+
method: 'POST',
|
|
364
|
+
url: '/api/v1/accounts/set_gender/',
|
|
365
|
+
form: this.client.request.sign({
|
|
366
|
+
_uuid: this.client.state.uuid,
|
|
367
|
+
gender,
|
|
368
|
+
}),
|
|
369
|
+
});
|
|
370
|
+
return response.body;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async setPrivate() {
|
|
375
|
+
return this.requestWithRetry(async () => {
|
|
376
|
+
const response = await this.client.request.send({
|
|
377
|
+
method: 'POST',
|
|
378
|
+
url: '/api/v1/accounts/set_private/',
|
|
379
|
+
form: this.client.request.sign({
|
|
380
|
+
_uuid: this.client.state.uuid,
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
return response.body;
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async setPublic() {
|
|
388
|
+
return this.requestWithRetry(async () => {
|
|
389
|
+
const response = await this.client.request.send({
|
|
390
|
+
method: 'POST',
|
|
391
|
+
url: '/api/v1/accounts/set_public/',
|
|
392
|
+
form: this.client.request.sign({
|
|
393
|
+
_uuid: this.client.state.uuid,
|
|
394
|
+
}),
|
|
395
|
+
});
|
|
396
|
+
return response.body;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async changePassword(oldPassword, newPassword) {
|
|
401
|
+
const oldEnc = this.encryptPassword(oldPassword);
|
|
402
|
+
const newEnc = this.encryptPassword(newPassword);
|
|
403
|
+
return this.requestWithRetry(async () => {
|
|
404
|
+
const response = await this.client.request.send({
|
|
405
|
+
method: 'POST',
|
|
406
|
+
url: '/api/v1/accounts/change_password/',
|
|
407
|
+
form: this.client.request.sign({
|
|
408
|
+
_uuid: this.client.state.uuid,
|
|
409
|
+
enc_old_password: #PWD_INSTAGRAM:4:${oldEnc.time}:${oldEnc.encrypted},
|
|
410
|
+
enc_new_password1: #PWD_INSTAGRAM:4:${newEnc.time}:${newEnc.encrypted},
|
|
411
|
+
enc_new_password2: #PWD_INSTAGRAM:4:${newEnc.time}:${newEnc.encrypted},
|
|
412
|
+
}),
|
|
413
|
+
});
|
|
414
|
+
return response.body;
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async sendConfirmEmail() {
|
|
419
|
+
return this.requestWithRetry(async () => {
|
|
420
|
+
const response = await this.client.request.send({
|
|
421
|
+
method: 'POST',
|
|
422
|
+
url: '/api/v1/accounts/send_confirm_email/',
|
|
423
|
+
form: this.client.request.sign({
|
|
424
|
+
_uuid: this.client.state.uuid,
|
|
425
|
+
send_source: 'edit_profile',
|
|
426
|
+
}),
|
|
427
|
+
});
|
|
428
|
+
return response.body;
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async sendConfirmPhoneNumber(phoneNumber) {
|
|
433
|
+
return this.requestWithRetry(async () => {
|
|
434
|
+
const response = await this.client.request.send({
|
|
435
|
+
method: 'POST',
|
|
436
|
+
url: '/api/v1/accounts/send_confirm_phone_number/',
|
|
437
|
+
form: this.client.request.sign({
|
|
438
|
+
_uuid: this.client.state.uuid,
|
|
439
|
+
phone_number: phoneNumber,
|
|
440
|
+
}),
|
|
441
|
+
});
|
|
442
|
+
return response.body;
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async profilePictureChange(uploadId) {
|
|
447
|
+
return this.requestWithRetry(async () => {
|
|
448
|
+
const response = await this.client.request.send({
|
|
449
|
+
method: 'POST',
|
|
450
|
+
url: '/api/v1/accounts/change_profile_picture/',
|
|
451
|
+
form: this.client.request.sign({
|
|
452
|
+
_uuid: this.client.state.uuid,
|
|
453
|
+
use_fbuploader: 'true',
|
|
454
|
+
upload_id: uploadId,
|
|
455
|
+
}),
|
|
456
|
+
});
|
|
457
|
+
return response.body;
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
async profilePictureRemove() {
|
|
462
|
+
return this.requestWithRetry(async () => {
|
|
463
|
+
const response = await this.client.request.send({
|
|
464
|
+
method: 'POST',
|
|
465
|
+
url: '/api/v1/accounts/remove_profile_picture/',
|
|
466
|
+
form: this.client.request.sign({
|
|
467
|
+
_uuid: this.client.state.uuid,
|
|
468
|
+
}),
|
|
469
|
+
});
|
|
470
|
+
return response.body;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async newsInbox() {
|
|
475
|
+
return this.requestWithRetry(async () => {
|
|
476
|
+
const response = await this.client.request.send({
|
|
477
|
+
method: 'GET',
|
|
478
|
+
url: '/api/v1/news/inbox/',
|
|
479
|
+
});
|
|
480
|
+
return response.body;
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async syncLoginExperiments() {
|
|
485
|
+
return this.requestWithRetry(async () => {
|
|
486
|
+
const response = await this.client.request.send({
|
|
487
|
+
method: 'POST',
|
|
488
|
+
url: '/api/v1/qe/sync/',
|
|
489
|
+
form: this.client.request.sign({
|
|
490
|
+
id: this.client.state.uuid,
|
|
491
|
+
server_config_retrieval: '1',
|
|
492
|
+
experiments: this.client.state.constants?.LOGIN_EXPERIMENTS,
|
|
493
|
+
}),
|
|
494
|
+
});
|
|
495
|
+
return response.body;
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async syncPostLoginExperiments() {
|
|
500
|
+
let userId;
|
|
501
|
+
try { userId = this.client.state.cookieUserId; } catch { userId = '0'; }
|
|
502
|
+
return this.requestWithRetry(async () => {
|
|
503
|
+
const response = await this.client.request.send({
|
|
504
|
+
method: 'POST',
|
|
505
|
+
url: '/api/v1/qe/sync/',
|
|
506
|
+
form: this.client.request.sign({
|
|
507
|
+
id: userId,
|
|
508
|
+
_uid: userId,
|
|
509
|
+
_uuid: this.client.state.uuid,
|
|
510
|
+
server_config_retrieval: '1',
|
|
511
|
+
experiments: this.client.state.constants?.EXPERIMENTS,
|
|
512
|
+
}),
|
|
513
|
+
});
|
|
514
|
+
return response.body;
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
async syncLauncher(preLogin = true) {
|
|
519
|
+
const data = {
|
|
520
|
+
id: this.client.state.uuid,
|
|
521
|
+
server_config_retrieval: '1',
|
|
522
|
+
};
|
|
523
|
+
if (!preLogin) {
|
|
524
|
+
try {
|
|
525
|
+
data._uid = this.client.state.cookieUserId;
|
|
526
|
+
data._uuid = this.client.state.uuid;
|
|
527
|
+
} catch {}
|
|
528
|
+
}
|
|
529
|
+
return this.requestWithRetry(async () => {
|
|
530
|
+
const response = await this.client.request.send({
|
|
531
|
+
method: 'POST',
|
|
532
|
+
url: '/api/v1/launcher/sync/',
|
|
533
|
+
form: this.client.request.sign(data),
|
|
534
|
+
});
|
|
535
|
+
return response.body;
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async syncDeviceFeatures() {
|
|
540
|
+
return this.requestWithRetry(async () => {
|
|
541
|
+
const response = await this.client.request.send({
|
|
542
|
+
method: 'POST',
|
|
543
|
+
url: '/api/v1/devices/sync/',
|
|
544
|
+
form: this.client.request.sign({
|
|
545
|
+
id: this.client.state.uuid,
|
|
546
|
+
server_config_retrieval: '1',
|
|
547
|
+
}),
|
|
548
|
+
});
|
|
549
|
+
return response.body;
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async getPrefillCandidates() {
|
|
554
|
+
return this.requestWithRetry(async () => {
|
|
555
|
+
const response = await this.client.request.send({
|
|
556
|
+
method: 'POST',
|
|
557
|
+
url: '/api/v1/accounts/get_prefill_candidates/',
|
|
558
|
+
form: this.client.request.sign({
|
|
559
|
+
android_device_id: this.client.state.deviceId,
|
|
560
|
+
phone_id: this.client.state.phoneId,
|
|
561
|
+
usages: '["account_recovery_omnibox"]',
|
|
562
|
+
device_id: this.client.state.uuid,
|
|
563
|
+
}),
|
|
564
|
+
});
|
|
565
|
+
return response.body;
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async contactPointPrefill(usage = 'prefill') {
|
|
570
|
+
return this.requestWithRetry(async () => {
|
|
571
|
+
const response = await this.client.request.send({
|
|
572
|
+
method: 'POST',
|
|
573
|
+
url: '/api/v1/accounts/contact_point_prefill/',
|
|
574
|
+
form: this.client.request.sign({
|
|
575
|
+
phone_id: this.client.state.phoneId,
|
|
576
|
+
usage,
|
|
577
|
+
}),
|
|
578
|
+
});
|
|
579
|
+
return response.body;
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async getZrToken(params = {}) {
|
|
584
|
+
return this.requestWithRetry(async () => {
|
|
585
|
+
const response = await this.client.request.send({
|
|
586
|
+
method: 'GET',
|
|
587
|
+
url: '/api/v1/zr/token/result/',
|
|
588
|
+
qs: {
|
|
589
|
+
device_id: this.client.state.deviceId,
|
|
590
|
+
custom_device_id: this.client.state.uuid,
|
|
591
|
+
fetch_reason: 'token_expired',
|
|
592
|
+
token_hash: '',
|
|
593
|
+
...params,
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
return response.body;
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async getConsentSignupConfig() {
|
|
601
|
+
return this.requestWithRetry(async () => {
|
|
602
|
+
const response = await this.client.request.send({
|
|
603
|
+
method: 'GET',
|
|
604
|
+
url: '/api/v1/consent/get_signup_config/',
|
|
605
|
+
qs: {
|
|
606
|
+
guid: this.client.state.uuid,
|
|
607
|
+
main_account_selected: false,
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
return response.body;
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
async sendRecoveryFlowEmail(query) {
|
|
615
|
+
return this.requestWithRetry(async () => {
|
|
616
|
+
const response = await this.client.request.send({
|
|
617
|
+
url: '/api/v1/accounts/send_recovery_flow_email/',
|
|
618
|
+
method: 'POST',
|
|
619
|
+
form: this.client.request.sign({
|
|
620
|
+
adid: '',
|
|
621
|
+
guid: this.client.state.uuid,
|
|
622
|
+
device_id: this.client.state.deviceId,
|
|
623
|
+
query,
|
|
624
|
+
}),
|
|
625
|
+
});
|
|
626
|
+
return response.body;
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
async sendRecoveryFlowSms(query) {
|
|
631
|
+
return this.requestWithRetry(async () => {
|
|
632
|
+
const response = await this.client.request.send({
|
|
633
|
+
url: '/api/v1/accounts/send_recovery_flow_sms/',
|
|
634
|
+
method: 'POST',
|
|
635
|
+
form: this.client.request.sign({
|
|
636
|
+
adid: '',
|
|
637
|
+
guid: this.client.state.uuid,
|
|
638
|
+
device_id: this.client.state.deviceId,
|
|
639
|
+
query,
|
|
640
|
+
}),
|
|
641
|
+
});
|
|
642
|
+
return response.body;
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
static generateAttestParams(state) {
|
|
647
|
+
// Use node-forge to create a more standard self-signed certificate chain.
|
|
648
|
+
// Note: This is still a self-signed chain and not hardware-backed attestation.
|
|
649
|
+
// It is more syntactically correct and includes standard X.509 extensions.
|
|
650
|
+
|
|
651
|
+
// Generate RSA keypair (2048 bits) using node-forge
|
|
652
|
+
const keypair = forge.pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001 });
|
|
653
|
+
|
|
654
|
+
// Create leaf certificate
|
|
655
|
+
const leafCert = AccountRepository._generateSelfSignedCertForge(keypair.privateKey, keypair.publicKey, 'Android Keystore Key');
|
|
656
|
+
|
|
657
|
+
// Create intermediate certificate (self-signed for simulation)
|
|
658
|
+
const intermediateKeypair = forge.pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001 });
|
|
659
|
+
const intermediateCert = AccountRepository._generateSelfSignedCertForge(intermediateKeypair.privateKey, intermediateKeypair.publicKey, 'TEE');
|
|
660
|
+
|
|
661
|
+
// Certificate chain: leaf + intermediate
|
|
662
|
+
const certificateChain = leafCert + '\n' + intermediateCert;
|
|
663
|
+
|
|
664
|
+
// challenge nonce
|
|
665
|
+
const challengeNonce = crypto.randomBytes(24).toString('base64url');
|
|
666
|
+
|
|
667
|
+
// Sign the challengeNonce with the private key (forge)
|
|
668
|
+
const md = forge.md.sha256.create();
|
|
669
|
+
md.update(challengeNonce, 'utf8');
|
|
670
|
+
const signedBytes = keypair.privateKey.sign(md);
|
|
671
|
+
const signedNonce = Buffer.from(signedBytes, 'binary').toString('base64');
|
|
672
|
+
|
|
673
|
+
// Compute key hash (sha256 of public key DER)
|
|
674
|
+
const publicKeyAsn1 = forge.pki.publicKeyToAsn1(keypair.publicKey);
|
|
675
|
+
const publicKeyDer = forge.asn1.toDer(publicKeyAsn1).getBytes();
|
|
676
|
+
const keyHash = crypto.createHash('sha256').update(Buffer.from(publicKeyDer, 'binary')).digest('hex');
|
|
677
|
+
|
|
678
|
+
return {
|
|
679
|
+
attestation: [{
|
|
680
|
+
version: 2,
|
|
681
|
+
type: 'keystore',
|
|
682
|
+
errors: [0],
|
|
683
|
+
challenge_nonce: challengeNonce,
|
|
684
|
+
signed_nonce: signedNonce,
|
|
685
|
+
key_hash: keyHash,
|
|
686
|
+
certificate_chain: certificateChain,
|
|
687
|
+
}],
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
static _generateSelfSignedCertForge(privateKeyForge, publicKeyForge, cn) {
|
|
693
|
+
// Build a certificate using node-forge with standard extensions
|
|
694
|
+
const cert = forge.pki.createCertificate();
|
|
695
|
+
cert.publicKey = publicKeyForge;
|
|
696
|
+
cert.serialNumber = (Math.floor(Math.random() * 1e16)).toString(16);
|
|
697
|
+
const now = new Date();
|
|
698
|
+
cert.validity.notBefore = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000);
|
|
699
|
+
cert.validity.notAfter = new Date(now.getTime() + 10 * 365 * 24 * 60 * 60 * 1000);
|
|
700
|
+
|
|
701
|
+
const attrs = [
|
|
702
|
+
{ name: 'commonName', value: cn },
|
|
703
|
+
{ name: 'organizationName', value: 'com.instagram.android' },
|
|
704
|
+
];
|
|
705
|
+
cert.setSubject(attrs);
|
|
706
|
+
cert.setIssuer(attrs);
|
|
707
|
+
|
|
708
|
+
cert.setExtensions([
|
|
709
|
+
{ name: 'basicConstraints', cA: false },
|
|
710
|
+
{ name: 'keyUsage', digitalSignature: true, keyEncipherment: true },
|
|
711
|
+
{ name: 'extKeyUsage', serverAuth: true, clientAuth: true },
|
|
712
|
+
{ name: 'subjectKeyIdentifier' },
|
|
713
|
+
]);
|
|
714
|
+
|
|
715
|
+
// Sign certificate with private key
|
|
716
|
+
cert.sign(privateKeyForge, forge.md.sha256.create());
|
|
717
|
+
|
|
718
|
+
// Convert to PEM
|
|
719
|
+
const pem = forge.pki.certificateToPem(cert);
|
|
720
|
+
return pem;
|
|
721
|
+
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
static createJazoest(input) {
|
|
725
|
+
const buf = Buffer.from(String(input || ''), 'ascii');
|
|
726
|
+
let sum = 0;
|
|
727
|
+
for (let i = 0; i < buf.byteLength; i++) {
|
|
728
|
+
sum += buf.readUInt8(i);
|
|
729
|
+
}
|
|
730
|
+
return 2${sum};
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
encryptPassword(password) {
|
|
734
|
+
if (!this.client.state.passwordEncryptionPubKey) {
|
|
735
|
+
return { time: Math.floor(Date.now() / 1000).toString(), encrypted: password };
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const randKey = crypto.randomBytes(32);
|
|
739
|
+
const iv = crypto.randomBytes(12);
|
|
740
|
+
|
|
741
|
+
const rsaEncrypted = crypto.publicEncrypt({
|
|
742
|
+
key: Buffer.from(this.client.state.passwordEncryptionPubKey, 'base64').toString(),
|
|
743
|
+
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
744
|
+
}, randKey);
|
|
745
|
+
|
|
746
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', randKey, iv);
|
|
747
|
+
const time = Math.floor(Date.now() / 1000).toString();
|
|
748
|
+
cipher.setAAD(Buffer.from(time));
|
|
749
|
+
|
|
750
|
+
const aesEncrypted = Buffer.concat([cipher.update(password, 'utf8'), cipher.final()]);
|
|
751
|
+
const sizeBuffer = Buffer.alloc(2, 0);
|
|
752
|
+
sizeBuffer.writeInt16LE(rsaEncrypted.byteLength, 0);
|
|
753
|
+
const authTag = cipher.getAuthTag();
|
|
754
|
+
|
|
755
|
+
return {
|
|
756
|
+
time,
|
|
757
|
+
encrypted: Buffer.concat([
|
|
758
|
+
Buffer.from([1, this.client.state.passwordEncryptionKeyId || 0]),
|
|
759
|
+
iv,
|
|
760
|
+
sizeBuffer,
|
|
761
|
+
rsaEncrypted,
|
|
762
|
+
authTag,
|
|
763
|
+
aesEncrypted
|
|
764
|
+
]).toString('base64')
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
async passwordPublicKeys() {
|
|
770
|
+
const response = await this.client.request.send({
|
|
771
|
+
method: 'GET',
|
|
772
|
+
url: '/api/v1/qe/sync/',
|
|
773
|
+
});
|
|
774
|
+
const headers = response.headers || {};
|
|
775
|
+
const keyId = parseInt(headers['ig-set-password-encryption-key-id'] || '0');
|
|
776
|
+
const pubKey = headers['ig-set-password-encryption-pub-key'] || '';
|
|
777
|
+
return { keyId, pubKey };
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
async setPresenceDisabled(disabled = true) {
|
|
781
|
+
return this.requestWithRetry(async () => {
|
|
782
|
+
const response = await this.client.request.send({
|
|
783
|
+
method: 'POST',
|
|
784
|
+
url: '/api/v1/accounts/set_presence_disabled/',
|
|
785
|
+
form: this.client.request.sign({
|
|
786
|
+
_uuid: this.client.state.uuid,
|
|
787
|
+
disabled: disabled ? '1' : '0',
|
|
788
|
+
}),
|
|
789
|
+
});
|
|
790
|
+
return response.body;
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async getCommentFilter() {
|
|
795
|
+
return this.requestWithRetry(async () => {
|
|
796
|
+
const response = await this.client.request.send({
|
|
797
|
+
method: 'GET',
|
|
798
|
+
url: '/api/v1/accounts/get_comment_filter/',
|
|
799
|
+
});
|
|
800
|
+
return response.body;
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
async setCommentFilter(configValue = 0) {
|
|
805
|
+
return this.requestWithRetry(async () => {
|
|
806
|
+
const response = await this.client.request.send({
|
|
807
|
+
method: 'POST',
|
|
808
|
+
url: '/api/v1/accounts/set_comment_filter/',
|
|
809
|
+
form: this.client.request.sign({
|
|
810
|
+
_uuid: this.client.state.uuid,
|
|
811
|
+
config_value: String(configValue),
|
|
812
|
+
}),
|
|
813
|
+
});
|
|
814
|
+
return response.body;
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
async pushPreferences(preferences = 'default') {
|
|
819
|
+
return this.requestWithRetry(async () => {
|
|
820
|
+
const response = await this.client.request.send({
|
|
821
|
+
method: 'POST',
|
|
822
|
+
url: '/api/v1/push/register/',
|
|
823
|
+
form: this.client.request.sign({
|
|
824
|
+
_uuid: this.client.state.uuid,
|
|
825
|
+
device_type: 'android_mqtt',
|
|
826
|
+
is_main_push_channel: 'true',
|
|
827
|
+
phone_id: this.client.state.phoneId,
|
|
828
|
+
device_token: '',
|
|
829
|
+
guid: this.client.state.uuid,
|
|
830
|
+
users: preferences,
|
|
831
|
+
}),
|
|
832
|
+
});
|
|
833
|
+
return response.body;
|
|
834
|
+
});
|
|
835
|
+
}
|
|
804
836
|
}
|
|
805
837
|
|
|
806
838
|
module.exports = AccountRepository;
|