nodejs-insta-private-api-mqt 1.3.88 → 1.3.90

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.
@@ -586,6 +586,487 @@ class AccountRepository extends Repository {
586
586
  }
587
587
  }
588
588
 
589
+ /**
590
+ * Step 1: launcher/mobileconfig to fetch mobile config + encryption keys.
591
+ * Best effort – errors are ignored.
592
+ */
593
+ async _launcherMobileConfig(preLogin = true) {
594
+ const nowSec = Math.floor(Date.now() / 1000);
595
+ const state = this.client.state || {};
596
+
597
+ const androidDeviceId =
598
+ state.androidDeviceId ||
599
+ state.deviceId ||
600
+ `android-${crypto.randomBytes(8).toString('hex')}`;
601
+
602
+ const familyDeviceId =
603
+ state.phoneId ||
604
+ state.familyDeviceId ||
605
+ (crypto.randomUUID
606
+ ? crypto.randomUUID()
607
+ : require('uuid').v4());
608
+
609
+ const qeDeviceId =
610
+ state.deviceId ||
611
+ state.qeDeviceId ||
612
+ (crypto.randomUUID
613
+ ? crypto.randomUUID()
614
+ : require('uuid').v4());
615
+
616
+ const deviceUUID = state.uuid || qeDeviceId;
617
+ const userId = state.cookieUserId || '0';
618
+
619
+ const bloksVersionId =
620
+ state.bloksVersionId ||
621
+ '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
622
+
623
+ const lang = state.language || 'ro_RO';
624
+ const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
625
+ const userAgent = this._resolveUserAgent();
626
+
627
+ const timezoneOffset =
628
+ typeof state.timezoneOffset === 'number'
629
+ ? state.timezoneOffset
630
+ : 7200;
631
+
632
+ const params = {
633
+ bool_opt_policy: '0',
634
+ mobileconfig: '',
635
+ api_version: '9',
636
+ client_context: '["opt,value_hash"]',
637
+ unit_type: '2',
638
+ use_case: 'STANDARD',
639
+ query_hash:
640
+ 'f00b9d0869db3969378d8d06bfccb24b5ef078012c8e199cba961cd5dfedaa88',
641
+ ts: String(nowSec),
642
+ _uid: userId,
643
+ device_id: deviceUUID,
644
+ _uuid: deviceUUID,
645
+ fetch_mode: 'CONFIG_SYNC_ONLY',
646
+ fetch_type: 'ASYNC_FULL',
647
+ };
648
+
649
+ const headers = {
650
+ 'accept-language': acceptLanguage,
651
+ 'content-type':
652
+ 'application/x-www-form-urlencoded; charset=UTF-8',
653
+ priority: 'u=3',
654
+
655
+ 'x-bloks-is-layout-rtl': 'false',
656
+ 'x-bloks-prism-ax-base-colors-enabled': 'false',
657
+ 'x-bloks-prism-button-version': 'CONTROL',
658
+ 'x-bloks-prism-colors-enabled': 'true',
659
+ 'x-bloks-prism-font-enabled': 'false',
660
+ 'x-bloks-prism-indigo-link-version': '0',
661
+ 'x-bloks-version-id': bloksVersionId,
662
+
663
+ 'x-fb-client-ip': 'True',
664
+ 'x-fb-connection-type': 'WIFI',
665
+ 'x-fb-server-cluster': 'True',
666
+ 'x-fb-network-properties':
667
+ 'VPN;Validated;LocalAddrs=/10.0.0.2,;',
668
+ 'x-fb-http-engine': 'MNS/TCP',
669
+ 'x-fb-rmd': 'state=URL_ELIGIBLE',
670
+
671
+ 'x-ig-android-id': androidDeviceId,
672
+ 'x-ig-app-id': String(
673
+ state.fbAnalyticsApplicationId || '567067343352427'
674
+ ),
675
+ 'x-ig-app-locale': lang,
676
+ 'x-ig-bandwidth-speed-kbps': (
677
+ Math.random() * 1500 +
678
+ 800
679
+ ).toFixed(3),
680
+ 'x-ig-bandwidth-totalbytes-b': '0',
681
+ 'x-ig-bandwidth-totaltime-ms': '0',
682
+ 'x-ig-client-endpoint':
683
+ 'LockoutFragment:dogfooding_lockout',
684
+ 'x-ig-capabilities': '3brTv10=',
685
+ 'x-ig-connection-type': 'WIFI',
686
+ 'x-ig-device-id': deviceUUID,
687
+ 'x-ig-device-languages': '{"system_languages":"ro-RO"}',
688
+ 'x-ig-device-locale': lang,
689
+ 'x-ig-family-device-id': familyDeviceId,
690
+ 'x-ig-mapped-locale': lang,
691
+ 'x-ig-nav-chain':
692
+ 'LockoutFragment:dogfooding_lockout:1:cold_start',
693
+ 'x-ig-salt-ids':
694
+ '220140399,332020310,974466465,974460658',
695
+ 'x-ig-timezone-offset': String(timezoneOffset),
696
+ 'x-ig-www-claim': state.igWWWClaim || '0',
697
+ 'x-mid':
698
+ state.mid ||
699
+ state.machineId ||
700
+ `aZ${crypto.randomBytes(8).toString('hex')}`,
701
+ 'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
702
+ Math.random() * 1000
703
+ )
704
+ .toString()
705
+ .padStart(3, '0')}`,
706
+ 'x-pigeon-session-id':
707
+ state.pigeonSessionId ||
708
+ `UFS-${crypto.randomBytes(16).toString('hex')}-M`,
709
+ 'x-tigon-is-retry': 'False',
710
+
711
+ 'accept-encoding': 'gzip, deflate, br',
712
+ 'user-agent': userAgent,
713
+ };
714
+
715
+ if (state.cookieUserId) {
716
+ headers['ig-intended-user-id'] = String(state.cookieUserId);
717
+ headers['ig-u-ds-user-id'] = String(state.cookieUserId);
718
+ }
719
+ if (state.igURur) {
720
+ headers['ig-u-rur'] = state.igURur;
721
+ }
722
+ if (state.authorization) {
723
+ headers['authorization'] = state.authorization;
724
+ }
725
+
726
+ try {
727
+ const response = await this.client.request.send({
728
+ method: 'POST',
729
+ url: '/api/v1/launcher/mobileconfig/',
730
+ form: this.client.request.sign(params),
731
+ headers,
732
+ });
733
+
734
+ // NEW: salvăm cheia de criptare a parolei direct din headers de la mobileconfig
735
+ try {
736
+ const mcHeaders = response.headers || {};
737
+ const mcLower = {};
738
+ for (const k of Object.keys(mcHeaders)) {
739
+ mcLower[k.toLowerCase()] = mcHeaders[k];
740
+ }
741
+
742
+ const mcKeyIdStr =
743
+ mcLower['ig-set-password-encryption-key-id'];
744
+ const mcPubKey =
745
+ mcLower['ig-set-password-encryption-pub-key'];
746
+
747
+ if (mcKeyIdStr) {
748
+ const parsedKeyId = parseInt(mcKeyIdStr, 10);
749
+ if (!Number.isNaN(parsedKeyId)) {
750
+ state.passwordEncryptionKeyId = parsedKeyId;
751
+ }
752
+ }
753
+
754
+ if (mcPubKey) {
755
+ state.passwordEncryptionPubKey = mcPubKey;
756
+ }
757
+ } catch (e) {}
758
+
759
+ try {
760
+ const fs = require('fs');
761
+ const path = require('path');
762
+ const debugDir = path.join(
763
+ process.cwd(),
764
+ 'authinfo_instagram'
765
+ );
766
+ const debugFile = path.join(
767
+ debugDir,
768
+ 'launcher-mobileconfig-debug.json'
769
+ );
770
+ try {
771
+ fs.mkdirSync(debugDir, { recursive: true });
772
+ } catch (e) {}
773
+ const debugPayload = {
774
+ at: new Date().toISOString(),
775
+ statusCode:
776
+ response.statusCode || response.status || null,
777
+ headers: response.headers || null,
778
+ body: response.body || null,
779
+ };
780
+ fs.writeFileSync(
781
+ debugFile,
782
+ JSON.stringify(debugPayload, null, 2),
783
+ 'utf8'
784
+ );
785
+ } catch (e) {}
786
+
787
+ return response.body;
788
+ } catch (e) {
789
+ return null;
790
+ }
791
+ }
792
+
793
+ /**
794
+ * Step 2: Android Keystore attestation – moves x-ig-attest-params
795
+ * payload to dedicated endpoint /api/v1/attestation/create_android_keystore/.
796
+ */
797
+ async _createAndroidKeystoreAttestation() {
798
+ const state = this.client.state || {};
799
+ const nowSec = Math.floor(Date.now() / 1000);
800
+
801
+ const androidDeviceId =
802
+ state.androidDeviceId ||
803
+ state.deviceId ||
804
+ `android-${crypto.randomBytes(8).toString('hex')}`;
805
+
806
+ const familyDeviceId =
807
+ state.phoneId ||
808
+ state.familyDeviceId ||
809
+ (crypto.randomUUID
810
+ ? crypto.randomUUID()
811
+ : require('uuid').v4());
812
+
813
+ const qeDeviceId =
814
+ state.deviceId ||
815
+ state.qeDeviceId ||
816
+ (crypto.randomUUID
817
+ ? crypto.randomUUID()
818
+ : require('uuid').v4());
819
+
820
+ const deviceUUID = state.uuid || qeDeviceId;
821
+ const userId = state.cookieUserId || '0';
822
+
823
+ const attestParams =
824
+ AccountRepository.generateAttestParams(state);
825
+
826
+ const lang = state.language || 'ro_RO';
827
+ const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
828
+ const userAgent = this._resolveUserAgent();
829
+ const timezoneOffset =
830
+ typeof state.timezoneOffset === 'number'
831
+ ? state.timezoneOffset
832
+ : 7200;
833
+
834
+ const params = {
835
+ _uid: userId,
836
+ _uuid: deviceUUID,
837
+ device_id: androidDeviceId,
838
+ family_device_id: familyDeviceId,
839
+ ts: String(nowSec),
840
+ ...attestParams,
841
+ };
842
+
843
+ const headers = {
844
+ 'accept-language': acceptLanguage,
845
+ 'content-type':
846
+ 'application/x-www-form-urlencoded; charset=UTF-8',
847
+ priority: 'u=3',
848
+
849
+ 'x-ig-android-id': androidDeviceId,
850
+ 'x-ig-device-id': qeDeviceId,
851
+ 'x-ig-family-device-id': familyDeviceId,
852
+ 'x-ig-timezone-offset': String(timezoneOffset),
853
+ 'x-ig-app-id': String(
854
+ state.fbAnalyticsApplicationId || '567067343352427'
855
+ ),
856
+ 'x-ig-app-locale': lang,
857
+ 'x-ig-device-locale': lang,
858
+ 'x-ig-mapped-locale': lang,
859
+ 'x-ig-connection-type': 'WIFI',
860
+ 'x-ig-capabilities': '3brTv10=',
861
+ 'x-ig-www-claim': state.igWWWClaim || '0',
862
+
863
+ 'x-pigeon-rawclienttime': `${nowSec}.${Math.floor(
864
+ Math.random() * 1000
865
+ )
866
+ .toString()
867
+ .padStart(3, '0')}`,
868
+ 'x-pigeon-session-id':
869
+ state.pigeonSessionId ||
870
+ `UFS-${crypto.randomBytes(16).toString('hex')}-A`,
871
+ 'x-mid':
872
+ state.mid ||
873
+ state.machineId ||
874
+ `aZ${crypto.randomBytes(8).toString('hex')}`,
875
+
876
+ 'x-fb-client-ip': 'True',
877
+ 'x-fb-connection-type': 'WIFI',
878
+ 'x-fb-server-cluster': 'True',
879
+ 'x-fb-http-engine': 'MNS/TCP',
880
+ 'x-fb-network-properties':
881
+ 'VPN;Validated;LocalAddrs=/10.0.0.2,;',
882
+ 'x-fb-rmd': 'state=URL_ELIGIBLE',
883
+
884
+ 'accept-encoding': 'gzip, deflate, br',
885
+ 'user-agent': userAgent,
886
+ };
887
+
888
+ if (state.cookieUserId) {
889
+ headers['ig-intended-user-id'] = String(state.cookieUserId);
890
+ }
891
+ if (state.authorization) {
892
+ headers['authorization'] = state.authorization;
893
+ }
894
+ if (state.igURur) {
895
+ headers['ig-u-rur'] = state.igURur;
896
+ }
897
+
898
+ try {
899
+ const response = await this.client.request.send({
900
+ method: 'POST',
901
+ url: '/api/v1/attestation/create_android_keystore/',
902
+ form: this.client.request.sign(params),
903
+ headers,
904
+ });
905
+
906
+ try {
907
+ const fs = require('fs');
908
+ const path = require('path');
909
+ const debugDir = path.join(
910
+ process.cwd(),
911
+ 'authinfo_instagram'
912
+ );
913
+ const debugFile = path.join(
914
+ debugDir,
915
+ 'attestation-debug.json'
916
+ );
917
+ try {
918
+ fs.mkdirSync(debugDir, { recursive: true });
919
+ } catch (e) {}
920
+ const debugPayload = {
921
+ at: new Date().toISOString(),
922
+ statusCode:
923
+ response.statusCode || response.status || null,
924
+ headers: response.headers || null,
925
+ body: response.body || null,
926
+ };
927
+ fs.writeFileSync(
928
+ debugFile,
929
+ JSON.stringify(debugPayload, null, 2),
930
+ 'utf8'
931
+ );
932
+ } catch (e) {}
933
+
934
+ return response.body;
935
+ } catch (e) {
936
+ return null;
937
+ }
938
+ }
939
+
940
+ /**
941
+ * Step 3 & 4 & 5 helpers: Terms of service preload, process client data,
942
+ * and (optional) phone number prefill.
943
+ * These are best-effort calls; failures are ignored.
944
+ */
945
+ async _preloadTermsOfService() {
946
+ const state = this.client.state || {};
947
+ const lang = state.language || 'ro_RO';
948
+ const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
949
+ const userAgent = this._resolveUserAgent();
950
+ const bloksVersionId =
951
+ state.bloksVersionId ||
952
+ '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
953
+
954
+ const paramsJson = JSON.stringify({});
955
+ const bkClientContext = JSON.stringify({
956
+ bloks_version: bloksVersionId,
957
+ styles_id: 'instagram',
958
+ });
959
+
960
+ const headers = {
961
+ 'accept-language': acceptLanguage,
962
+ 'content-type':
963
+ 'application/x-www-form-urlencoded; charset=UTF-8',
964
+ priority: 'u=3',
965
+ 'x-bloks-version-id': bloksVersionId,
966
+ 'user-agent': userAgent,
967
+ };
968
+
969
+ try {
970
+ const response = await this.client.request.send({
971
+ method: 'POST',
972
+ url: '/api/v1/bloks/apps/com.bloks.www.caa.login.oxygen_preloads_terms_of_service/',
973
+ form: {
974
+ params: paramsJson,
975
+ bk_client_context: bkClientContext,
976
+ bloks_versioning_id: bloksVersionId,
977
+ },
978
+ headers,
979
+ });
980
+ return response.body;
981
+ } catch (e) {
982
+ return null;
983
+ }
984
+ }
985
+
986
+ async _processClientDataAndRedirect(username) {
987
+ const state = this.client.state || {};
988
+ const lang = state.language || 'ro_RO';
989
+ const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
990
+ const userAgent = this._resolveUserAgent();
991
+ const bloksVersionId =
992
+ state.bloksVersionId ||
993
+ '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
994
+
995
+ const paramsJson = JSON.stringify({
996
+ username_input: username || '',
997
+ });
998
+ const bkClientContext = JSON.stringify({
999
+ bloks_version: bloksVersionId,
1000
+ styles_id: 'instagram',
1001
+ });
1002
+
1003
+ const headers = {
1004
+ 'accept-language': acceptLanguage,
1005
+ 'content-type':
1006
+ 'application/x-www-form-urlencoded; charset=UTF-8',
1007
+ priority: 'u=3',
1008
+ 'x-bloks-version-id': bloksVersionId,
1009
+ 'user-agent': userAgent,
1010
+ };
1011
+
1012
+ try {
1013
+ const response = await this.client.request.send({
1014
+ method: 'POST',
1015
+ url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.login.process_client_data_and_redirect/',
1016
+ form: {
1017
+ params: paramsJson,
1018
+ bk_client_context: bkClientContext,
1019
+ bloks_versioning_id: bloksVersionId,
1020
+ },
1021
+ headers,
1022
+ });
1023
+ return response.body;
1024
+ } catch (e) {
1025
+ return null;
1026
+ }
1027
+ }
1028
+
1029
+ async _phoneNumberPrefill() {
1030
+ const state = this.client.state || {};
1031
+ const lang = state.language || 'ro_RO';
1032
+ const acceptLanguage = `${lang.replace('_', '-')}, en-US`;
1033
+ const userAgent = this._resolveUserAgent();
1034
+ const bloksVersionId =
1035
+ state.bloksVersionId ||
1036
+ '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
1037
+
1038
+ const paramsJson = JSON.stringify({});
1039
+ const bkClientContext = JSON.stringify({
1040
+ bloks_version: bloksVersionId,
1041
+ styles_id: 'instagram',
1042
+ });
1043
+
1044
+ const headers = {
1045
+ 'accept-language': acceptLanguage,
1046
+ 'content-type':
1047
+ 'application/x-www-form-urlencoded; charset=UTF-8',
1048
+ priority: 'u=3',
1049
+ 'x-bloks-version-id': bloksVersionId,
1050
+ 'user-agent': userAgent,
1051
+ };
1052
+
1053
+ try {
1054
+ const response = await this.client.request.send({
1055
+ method: 'POST',
1056
+ url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.phone.number.prefill.async.controller/',
1057
+ form: {
1058
+ params: paramsJson,
1059
+ bk_client_context: bkClientContext,
1060
+ bloks_versioning_id: bloksVersionId,
1061
+ },
1062
+ headers,
1063
+ });
1064
+ return response.body;
1065
+ } catch (e) {
1066
+ return null;
1067
+ }
1068
+ }
1069
+
589
1070
  async login(credentialsOrUsername, passwordArg) {
590
1071
  let username, password;
591
1072
  if (
@@ -603,18 +1084,42 @@ class AccountRepository extends Repository {
603
1084
  throw new Error('Username and password are required');
604
1085
  }
605
1086
 
1087
+ // 0: ne asigurăm că avem csrf + cookies de bază
1088
+ await this.ensureCsrfToken();
1089
+
1090
+ // 1: încercăm să luăm cheia de criptare a parolei din /launcher/mobileconfig/
1091
+ try {
1092
+ await this._launcherMobileConfig(true);
1093
+ } catch (e) {}
1094
+
1095
+ // Fallback: dacă mobileconfig nu a setat cheia, folosim mecanismul vechi de LOGIN_EXPERIMENTS
606
1096
  if (!this.client.state.passwordEncryptionPubKey) {
607
1097
  await this.syncLoginExperiments();
608
1098
  }
609
1099
 
1100
+ // Acum parola este criptată CU cheia venită din mobileconfig (dacă a existat)
610
1101
  const { encrypted, time } = this.encryptPassword(password);
611
1102
 
612
- await this.ensureCsrfToken();
1103
+ // Step 2–5: keystore attestation, TOS preload, process client data, phone prefill
1104
+ try {
1105
+ await this._createAndroidKeystoreAttestation();
1106
+ } catch (e) {}
1107
+ try {
1108
+ await this._preloadTermsOfService();
1109
+ } catch (e) {}
1110
+ try {
1111
+ await this._processClientDataAndRedirect(username);
1112
+ } catch (e) {}
1113
+ try {
1114
+ await this._phoneNumberPrefill();
1115
+ } catch (e) {}
613
1116
 
1117
+ // Step 6: OAuth token fetch
614
1118
  try {
615
1119
  await this._prefetchOauthTokenForLogin(username);
616
1120
  } catch (e) {}
617
1121
 
1122
+ // Step 7: real login
618
1123
  return this.requestWithRetry(async () => {
619
1124
  const nowSec = Math.floor(Date.now() / 1000);
620
1125
  const aacInitTimestamp =
@@ -754,10 +1259,6 @@ class AccountRepository extends Repository {
754
1259
  server_params: serverParams,
755
1260
  });
756
1261
 
757
- const attestParams =
758
- AccountRepository.generateAttestParams(
759
- this.client.state
760
- );
761
1262
  const bloksVersionId =
762
1263
  this.client.state.bloksVersionId ||
763
1264
  '5e47baf35c5a270b44c8906c8b99063564b30ef69779f3dee0b828bee2e4ef5b';
@@ -812,7 +1313,6 @@ class AccountRepository extends Repository {
812
1313
  '567067343352427'
813
1314
  ),
814
1315
  'x-ig-app-locale': lang,
815
- 'x-ig-attest-params': JSON.stringify(attestParams),
816
1316
  'x-ig-bandwidth-speed-kbps': (
817
1317
  Math.random() * 1500 +
818
1318
  800
@@ -1821,7 +2321,7 @@ class AccountRepository extends Repository {
1821
2321
  phone_id: this.client.state.phoneId,
1822
2322
  device_token: '',
1823
2323
  guid: this.client.state.uuid,
1824
- users: preferences,
2324
+ users: preferen1gces,
1825
2325
  }),
1826
2326
  });
1827
2327
  return response.body;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-insta-private-api-mqt",
3
- "version": "1.3.88",
3
+ "version": "1.3.90",
4
4
  "description": "Complete Instagram MQTT protocol with full-featured REALTIME and REST API — all in one project.",
5
5
 
6
6
  "main": "dist/dist/index.js",