-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlogin.js
More file actions
105 lines (75 loc) · 3.52 KB
/
login.js
File metadata and controls
105 lines (75 loc) · 3.52 KB
1
2
3
4
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
/**
* InstaScripts
* A Collection of Instagram Facilitator Scripts
* https://github.com/ashenm/instascripts
*
* Ashen Gunaratne
* mail@ashenm.ml
*
*/
const qs = require('qs');
const axios = require('axios');
const cookies = require('cookie');
const prompts = require('prompts');
const assert = require('assert');
const fs = require('fs');
const CACHE = 'credentials.json';
module.exports = async function () {
let cookie;
let response;
try {
fs.accessSync(CACHE);
cookie = JSON.parse(fs.readFileSync(CACHE));
// validate expiry of saved session
response = await axios.post('https://www.instagram.com/accounts/get_encrypted_credentials/', {}, {
headers: { 'Cookie': qs.stringify(cookie).replace(/&/g, '; '), 'X-CSRFToken': cookie.csrftoken } });
assert(response.status === 200 && response.data.status === 'ok');
console.info('[INFO] Using saved session from %s', fs.realpathSync(CACHE));
return cookie;
} catch (e) {
// TODO standardise cookie management interface
cookie = Object.defineProperties({}, {
'Path': { enumerable: false, writable: true },
'Max-Age': { enumerable: false, writable: true },
'Domain': { enumerable: false, writable: true }
});
}
try {
// initial nonce
response = await axios.get('https://www.instagram.com/');
[ cookie.csrftoken ] = response.data.match(/(?<="csrf_token":")\w+/);
// attempt login
response = await axios.post('https://www.instagram.com/accounts/login/ajax/', qs.stringify({
username: (await prompts({ type: 'text', name: 'username', message: 'Username' })).username,
password: (await prompts({ type: 'password', name: 'password', message: 'Password' })).password
}), { headers: { 'X-CSRFToken': cookie.csrftoken }, validateStatus: null });
// ensure successful authentication
assert(response.status === 200 || response.status === 400,
JSON.stringify({ code: response.status, message: require('http').STATUS_CODES[response.status] }));
assert(response.headers.hasOwnProperty('set-cookie'), '{ "code": 403, "message": "Forbidden" }');
// extract cookies
cookie = response.headers['set-cookie'].reduce((accumulator, value) => Object.assign(accumulator, cookies.parse(value)), cookie);
// handle two-factor authentication
// TODO handle sms-based auth mechanisms
if (response.data.two_factor_required) {
response = await axios.post('https://www.instagram.com/accounts/login/ajax/two_factor/', qs.stringify({
username: response.data.two_factor_info.username,
verificationCode: (await prompts({ type: 'text', name: 'PIN', message: 'Security Code' })).PIN,
identifier: response.data.two_factor_info.two_factor_identifier
}), { headers: { 'X-CSRFToken': cookie.csrftoken, 'Cookie': qs.stringify(cookie).replace(/&/g, '; ') }, validateStatus: null });
}
assert(response.data.authenticated && response.headers.hasOwnProperty('set-cookie'), '{ "code": "403", "message": "Forbidden" }');
cookie = response.headers['set-cookie'].reduce((accumulator, value) => Object.assign(accumulator, cookies.parse(value)), cookie);
// TODO parametrise caching
// TODO handle i/o error(s)
fs.writeFileSync(CACHE, JSON.stringify(cookie), { mode: 0o600 });
console.info('[INFO] Saving session to %s', fs.realpathSync(CACHE));
return cookie;
} catch (e) {
return { error: JSON.parse(e.message) };
}
};
if (require.main === module) {
module.exports().then(console.info);
}
/* vim: set expandtab shiftwidth=2 syntax=javascript: */