nodejs-insta-private-api-mqt 1.3.74 → 1.3.76
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/core/request.js
CHANGED
|
@@ -264,7 +264,7 @@ class Request {
|
|
|
264
264
|
if (!url) return;
|
|
265
265
|
const path = url.toLowerCase();
|
|
266
266
|
|
|
267
|
-
if (path.includes('/accounts/login/') || path.includes('/launcher/sync/')) {
|
|
267
|
+
if (path.includes('/accounts/login/') || path.includes('/launcher/sync/') || path.includes('/bloks/async_action/com.bloks.www.bloks.caa.login')) {
|
|
268
268
|
this.navChain.simulateAppOpen();
|
|
269
269
|
return;
|
|
270
270
|
}
|
package/dist/core/state.js
CHANGED
|
@@ -188,11 +188,19 @@ class State {
|
|
|
188
188
|
} catch {
|
|
189
189
|
// fallback to parsed authorization if available
|
|
190
190
|
this.updateAuthorization();
|
|
191
|
-
if (
|
|
192
|
-
|
|
191
|
+
if (this.parsedAuthorization && this.parsedAuthorization.ds_user_id) {
|
|
192
|
+
return this.parsedAuthorization.ds_user_id;
|
|
193
|
+
}
|
|
194
|
+
// fallback: check if userId was set directly on state
|
|
195
|
+
if (this._userId) return this._userId;
|
|
196
|
+
return null;
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
199
|
|
|
200
|
+
set cookieUserId(val) {
|
|
201
|
+
this._userId = val;
|
|
202
|
+
}
|
|
203
|
+
|
|
196
204
|
get cookieUsername() {
|
|
197
205
|
try {
|
|
198
206
|
return this.extractCookieValue('ds_user');
|
|
@@ -45,22 +45,84 @@ class AccountRepository extends Repository {
|
|
|
45
45
|
const { encrypted, time } = this.encryptPassword(password);
|
|
46
46
|
|
|
47
47
|
return this.requestWithRetry(async () => {
|
|
48
|
-
const
|
|
48
|
+
const aacInitTimestamp = Math.floor(Date.now() / 1000) - Math.floor(Math.random() * 50);
|
|
49
|
+
const aacjid = crypto.randomUUID ? crypto.randomUUID() : require('uuid').v4();
|
|
50
|
+
const aaccs = crypto.randomBytes(32).toString('base64url');
|
|
51
|
+
|
|
52
|
+
const clientInputParams = {
|
|
53
|
+
aac: JSON.stringify({
|
|
54
|
+
aac_init_timestamp: aacInitTimestamp,
|
|
55
|
+
aacjid: aacjid,
|
|
56
|
+
aaccs: aaccs,
|
|
57
|
+
}),
|
|
58
|
+
sim_phones: [],
|
|
59
|
+
aymh_accounts: [],
|
|
60
|
+
network_bssid: null,
|
|
61
|
+
secure_family_device_id: '',
|
|
62
|
+
has_granted_read_contacts_permissions: 0,
|
|
63
|
+
auth_secure_device_id: '',
|
|
64
|
+
has_whatsapp_installed: 1,
|
|
65
|
+
password: `#PWD_INSTAGRAM:4:${time}:${encrypted}`,
|
|
66
|
+
sso_token_map_json_string: '{}',
|
|
67
|
+
block_store_machine_id: '',
|
|
68
|
+
ig_vetted_device_nonces: '{}',
|
|
69
|
+
contact_point: username,
|
|
70
|
+
machine_id: this.client.state.mid || '',
|
|
71
|
+
login_attempt_count: '0',
|
|
72
|
+
reg_flow_taken: 'phone',
|
|
73
|
+
device_id: this.client.state.uuid,
|
|
74
|
+
phone_id: this.client.state.phoneId,
|
|
75
|
+
family_device_id: this.client.state.phoneId,
|
|
76
|
+
encryption_enabled: '1',
|
|
77
|
+
has_dbl_tap_login: 0,
|
|
78
|
+
jazoest: AccountRepository.createJazoest(this.client.state.phoneId),
|
|
79
|
+
openid_tokens: '{}',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const serverParams = {
|
|
83
|
+
credential_type: 'password',
|
|
84
|
+
device_id: this.client.state.uuid,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const paramsJson = JSON.stringify({
|
|
88
|
+
client_input_params: clientInputParams,
|
|
89
|
+
server_params: serverParams,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const attestParams = AccountRepository.generateAttestParams(this.client.state);
|
|
93
|
+
|
|
94
|
+
const bloksHeaders = {
|
|
95
|
+
'X-Bloks-Prism-Ax-Base-Colors-Enabled': 'false',
|
|
96
|
+
'X-Bloks-Prism-Button-Version': 'CONTROL',
|
|
97
|
+
'X-Bloks-Prism-Colors-Enabled': 'true',
|
|
98
|
+
'X-Bloks-Prism-Font-Enabled': 'false',
|
|
99
|
+
'X-Bloks-Prism-Indigo-Link-Version': '0',
|
|
100
|
+
'X-FB-Friendly-Name': 'IgApi: bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
|
|
101
|
+
'X-FB-Request-Analytics-Tags': JSON.stringify({
|
|
102
|
+
network_tags: {
|
|
103
|
+
product: this.client.state.fbAnalyticsApplicationId,
|
|
104
|
+
purpose: 'fetch',
|
|
105
|
+
surface: 'undefined',
|
|
106
|
+
request_category: 'api',
|
|
107
|
+
retry_attempt: '0',
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
'X-IG-Client-Endpoint': 'com.bloks.www.caa.login.aymh_single_profile_screen_entry',
|
|
111
|
+
'X-Tigon-Is-Retry': 'False',
|
|
112
|
+
'X-FB-RMD': 'state=URL_ELIGIBLE',
|
|
113
|
+
'X-IG-Attest-Params': JSON.stringify(attestParams),
|
|
114
|
+
'X-FB-Connection-Type': 'MOBILE.LTE',
|
|
115
|
+
'X-FB-Network-Properties': 'VPN;Metered;Validated;LocalAddrs=/10.0.0.2,;',
|
|
116
|
+
'X-FB-Conn-UUID-Client': crypto.randomBytes(16).toString('hex'),
|
|
117
|
+
'Accept-Encoding': 'zstd',
|
|
118
|
+
'X-FB-HTTP-Engine': 'MNS/TCP',
|
|
119
|
+
};
|
|
120
|
+
|
|
49
121
|
const response = await this.client.request.send({
|
|
50
122
|
method: 'POST',
|
|
51
|
-
url: '/api/v1/
|
|
52
|
-
form:
|
|
53
|
-
|
|
54
|
-
country_codes: JSON.stringify([{ country_code: String(countryCode), source: ['default'] }]),
|
|
55
|
-
phone_id: this.client.state.phoneId,
|
|
56
|
-
enc_password: `#PWD_INSTAGRAM:4:${time}:${encrypted}`,
|
|
57
|
-
username,
|
|
58
|
-
adid: this.client.state.adid,
|
|
59
|
-
guid: this.client.state.uuid,
|
|
60
|
-
device_id: this.client.state.deviceId,
|
|
61
|
-
google_tokens: '[]',
|
|
62
|
-
login_attempt_count: '0',
|
|
63
|
-
}),
|
|
123
|
+
url: '/api/v1/bloks/async_action/com.bloks.www.bloks.caa.login.async.send_login_request/',
|
|
124
|
+
form: { params: paramsJson },
|
|
125
|
+
headers: bloksHeaders,
|
|
64
126
|
});
|
|
65
127
|
|
|
66
128
|
const body = response.body;
|
|
@@ -88,7 +150,51 @@ class AccountRepository extends Repository {
|
|
|
88
150
|
throw err;
|
|
89
151
|
}
|
|
90
152
|
|
|
91
|
-
|
|
153
|
+
if (body.layout) {
|
|
154
|
+
const layoutStr = typeof body.layout === 'string' ? body.layout : JSON.stringify(body.layout);
|
|
155
|
+
|
|
156
|
+
if (layoutStr.includes('two_factor_required') || layoutStr.includes('"two_factor_info"')) {
|
|
157
|
+
let twoFactorInfo = null;
|
|
158
|
+
try {
|
|
159
|
+
const match = layoutStr.match(/"two_factor_info"\s*:\s*(\{[^}]+\})/);
|
|
160
|
+
if (match) twoFactorInfo = JSON.parse(match[1]);
|
|
161
|
+
} catch (e) {}
|
|
162
|
+
const err = new Error('Two factor authentication required');
|
|
163
|
+
err.name = 'IgLoginTwoFactorRequiredError';
|
|
164
|
+
err.twoFactorInfo = twoFactorInfo;
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (layoutStr.includes('bad_password') || layoutStr.includes('incorrect_password')) {
|
|
169
|
+
const err = new Error('Bad password');
|
|
170
|
+
err.name = 'IgLoginBadPasswordError';
|
|
171
|
+
throw err;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (layoutStr.includes('invalid_user') || layoutStr.includes('user_not_found')) {
|
|
175
|
+
const err = new Error('Invalid user');
|
|
176
|
+
err.name = 'IgLoginInvalidUserError';
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (layoutStr.includes('challenge_required')) {
|
|
181
|
+
const err = new Error('Challenge required');
|
|
182
|
+
err.name = 'IgCheckpointError';
|
|
183
|
+
err.challengeInfo = body.challenge || null;
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (body.logged_in_user) {
|
|
189
|
+
return body.logged_in_user;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const userInfo = await this.currentUser();
|
|
194
|
+
return userInfo.user || userInfo;
|
|
195
|
+
} catch (e) {
|
|
196
|
+
return body;
|
|
197
|
+
}
|
|
92
198
|
});
|
|
93
199
|
}
|
|
94
200
|
|
|
@@ -459,6 +565,96 @@ class AccountRepository extends Repository {
|
|
|
459
565
|
});
|
|
460
566
|
}
|
|
461
567
|
|
|
568
|
+
static generateAttestParams(state) {
|
|
569
|
+
const challengeNonce = crypto.randomBytes(24).toString('base64url');
|
|
570
|
+
|
|
571
|
+
const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {
|
|
572
|
+
namedCurve: 'prime256v1',
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const signedData = crypto.sign(null, Buffer.from(challengeNonce), privateKey);
|
|
576
|
+
const signedNonce = signedData.toString('base64');
|
|
577
|
+
|
|
578
|
+
const publicKeyDer = publicKey.export({ type: 'spki', format: 'der' });
|
|
579
|
+
const keyHash = crypto.createHash('sha256').update(publicKeyDer).digest('hex');
|
|
580
|
+
|
|
581
|
+
const leafCertPem = AccountRepository._generateSelfSignedCert(privateKey, publicKey, 'Android Keystore Key');
|
|
582
|
+
const intermediateCertPem = AccountRepository._generateSelfSignedCert(privateKey, publicKey, 'TEE');
|
|
583
|
+
|
|
584
|
+
const certificateChain = leafCertPem + '\n' + intermediateCertPem;
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
attestation: [{
|
|
588
|
+
version: 2,
|
|
589
|
+
type: 'keystore',
|
|
590
|
+
errors: [0],
|
|
591
|
+
challenge_nonce: challengeNonce,
|
|
592
|
+
signed_nonce: signedNonce,
|
|
593
|
+
key_hash: keyHash,
|
|
594
|
+
certificate_chain: certificateChain,
|
|
595
|
+
}],
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
static _generateSelfSignedCert(privateKey, publicKey, cn) {
|
|
600
|
+
const publicKeyDer = publicKey.export({ type: 'spki', format: 'der' });
|
|
601
|
+
const serialNumber = crypto.randomBytes(8);
|
|
602
|
+
const now = new Date();
|
|
603
|
+
const notBefore = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000);
|
|
604
|
+
const notAfter = new Date(now.getTime() + 10 * 365 * 24 * 60 * 60 * 1000);
|
|
605
|
+
|
|
606
|
+
const cnBytes = Buffer.from(cn, 'utf8');
|
|
607
|
+
const cnSeq = Buffer.concat([
|
|
608
|
+
Buffer.from([0x30, cnBytes.length + 13]),
|
|
609
|
+
Buffer.from([0x31, cnBytes.length + 11]),
|
|
610
|
+
Buffer.from([0x30, cnBytes.length + 9]),
|
|
611
|
+
Buffer.from([0x06, 0x03, 0x55, 0x04, 0x03]),
|
|
612
|
+
Buffer.from([0x0c, cnBytes.length]),
|
|
613
|
+
cnBytes,
|
|
614
|
+
]);
|
|
615
|
+
|
|
616
|
+
function encodeTime(date) {
|
|
617
|
+
const str = date.toISOString().replace(/[-:T]/g, '').slice(2, 14) + 'Z';
|
|
618
|
+
return Buffer.concat([Buffer.from([0x17, str.length]), Buffer.from(str)]);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const validityBuf = Buffer.concat([
|
|
622
|
+
encodeTime(notBefore),
|
|
623
|
+
encodeTime(notAfter),
|
|
624
|
+
]);
|
|
625
|
+
const validity = Buffer.concat([Buffer.from([0x30, validityBuf.length]), validityBuf]);
|
|
626
|
+
|
|
627
|
+
const tbs = Buffer.concat([
|
|
628
|
+
Buffer.from([0xa0, 0x03, 0x02, 0x01, 0x02]),
|
|
629
|
+
Buffer.from([0x02, serialNumber.length]), serialNumber,
|
|
630
|
+
Buffer.from([0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]),
|
|
631
|
+
cnSeq,
|
|
632
|
+
validity,
|
|
633
|
+
cnSeq,
|
|
634
|
+
publicKeyDer,
|
|
635
|
+
]);
|
|
636
|
+
|
|
637
|
+
const tbsSeq = Buffer.concat([Buffer.from([0x30, 0x82]), Buffer.alloc(2), tbs]);
|
|
638
|
+
tbsSeq.writeUInt16BE(tbs.length, 2);
|
|
639
|
+
|
|
640
|
+
const signature = crypto.sign(null, tbsSeq, privateKey);
|
|
641
|
+
|
|
642
|
+
const sigBitString = Buffer.concat([
|
|
643
|
+
Buffer.from([0x03, signature.length + 1, 0x00]),
|
|
644
|
+
signature,
|
|
645
|
+
]);
|
|
646
|
+
|
|
647
|
+
const algId = Buffer.from([0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]);
|
|
648
|
+
|
|
649
|
+
const certBody = Buffer.concat([tbsSeq, algId, sigBitString]);
|
|
650
|
+
const cert = Buffer.concat([Buffer.from([0x30, 0x82]), Buffer.alloc(2), certBody]);
|
|
651
|
+
cert.writeUInt16BE(certBody.length, 2);
|
|
652
|
+
|
|
653
|
+
const b64 = cert.toString('base64');
|
|
654
|
+
const lines = b64.match(/.{1,76}/g) || [b64];
|
|
655
|
+
return '-----BEGIN CERTIFICATE-----\n' + lines.join('\n') + '\n-----END CERTIFICATE-----';
|
|
656
|
+
}
|
|
657
|
+
|
|
462
658
|
static createJazoest(input) {
|
|
463
659
|
const buf = Buffer.from(input, 'ascii');
|
|
464
660
|
let sum = 0;
|
|
@@ -1228,7 +1228,13 @@ async function extractStateData(igState) {
|
|
|
1228
1228
|
const cookieFields = await extractCookieFields(igState);
|
|
1229
1229
|
|
|
1230
1230
|
// Primary identifiers
|
|
1231
|
-
|
|
1231
|
+
let userIdFromState = null;
|
|
1232
|
+
try {
|
|
1233
|
+
userIdFromState = igState.cookieUserId || null;
|
|
1234
|
+
} catch (e) {}
|
|
1235
|
+
if (!userIdFromState) {
|
|
1236
|
+
userIdFromState = igState.userId || igState.user_id || null;
|
|
1237
|
+
}
|
|
1232
1238
|
const dsUserIdFromCookies = cookieFields.dsUserIdCookie || igState.dsUserId || igState.ds_user_id || null;
|
|
1233
1239
|
const sessionIdFromCookies = cookieFields.sessionIdCookie || null;
|
|
1234
1240
|
const csrfFromCookies = cookieFields.csrfTokenCookie || null;
|
|
@@ -1297,7 +1303,9 @@ async function applyStateData(igState, authState) {
|
|
|
1297
1303
|
|
|
1298
1304
|
// apply new creds-derived fields if state supports them (non-intrusive)
|
|
1299
1305
|
try {
|
|
1300
|
-
|
|
1306
|
+
let _hasCookieUserId = false;
|
|
1307
|
+
try { _hasCookieUserId = !!igState.cookieUserId; } catch (e) {}
|
|
1308
|
+
if (creds.userId && !_hasCookieUserId) igState.cookieUserId = creds.userId;
|
|
1301
1309
|
if (creds.username && !igState.username) igState.username = creds.username;
|
|
1302
1310
|
if (creds.rankToken && !igState.rankToken) igState.rankToken = creds.rankToken;
|
|
1303
1311
|
if (creds.sessionId && !igState.sessionId) igState.sessionId = creds.sessionId;
|
package/package.json
CHANGED