UNPKG

@inite/n8n-nodes-instagram-private-api

Version:
347 lines (346 loc) 16.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InstagramAuth = void 0; const instagram_private_api_1 = require("instagram-private-api"); class InstagramAuth { constructor() { this.description = { displayName: 'Instagram Auth', name: 'instagramAuth', icon: 'file:instagram.svg', group: ['transform'], version: 1, subtitle: '={{$parameter["operation"]}}', description: 'Handle Instagram authentication', defaults: { name: 'Instagram Auth', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'instagramPrivateApi', required: true, }, ], properties: [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, options: [ { name: 'Login', value: 'login', description: 'Login to Instagram', action: 'Login to Instagram', }, { name: 'Submit 2FA Code', value: 'submit2FA', description: 'Submit two-factor authentication code', action: 'Submit two factor authentication code', }, { name: 'Load Session', value: 'loadSession', description: 'Load existing session', action: 'Load existing session', }, ], default: 'login', }, { displayName: 'Session Data', name: 'sessionData', type: 'string', default: '', description: 'Previously saved session data', displayOptions: { show: { operation: ['loadSession'], }, }, }, { displayName: '2FA Code', name: 'twoFactorCode', type: 'string', default: '', required: true, description: 'Two-factor authentication code from SMS or TOTP (Google Authenticator)', displayOptions: { show: { operation: ['submit2FA'], }, }, }, { displayName: 'Verification Method', name: 'verificationMethod', type: 'options', options: [ { name: 'SMS', value: '1', description: 'Use SMS verification', }, { name: 'TOTP (Google Authenticator)', value: '0', description: 'Use TOTP verification (Google Authenticator)', }, ], default: '0', description: 'Method to use for verification', displayOptions: { show: { operation: ['submit2FA'], }, }, }, { displayName: 'Trust This Device', name: 'trustThisDevice', type: 'boolean', default: true, description: 'Whether to trust this device for future logins', displayOptions: { show: { operation: ['submit2FA'], }, }, }, { displayName: 'Two Factor Identifier', name: 'twoFactorIdentifier', type: 'string', default: '', description: 'Two factor identifier from previous login step', displayOptions: { show: { operation: ['submit2FA'], }, }, }, { displayName: 'Username', name: 'username', type: 'string', default: '', description: 'Username from previous login step', displayOptions: { show: { operation: ['submit2FA'], }, }, }, ], }; } async execute() { var _a, _b, _c, _d; const items = this.getInputData(); const returnData = []; const operation = this.getNodeParameter('operation', 0); const credentials = await this.getCredentials('instagramPrivateApi'); const ig = new instagram_private_api_1.IgApiClient(); ig.state.generateDevice(credentials.username); // Create a promise to capture the session data let sessionPromise = null; let sessionResolver = null; // Set up state management with a promise to ensure we capture the session ig.request.end$.subscribe(async () => { try { const serialized = await ig.state.serialize(); delete serialized.constants; // Use library's version info // Store the session data in the return data if (returnData.length > 0) { returnData[0] = { ...returnData[0], sessionData: serialized }; } else { returnData.push({ sessionData: serialized }); } // Resolve the promise if it exists if (sessionResolver) { sessionResolver(serialized); sessionResolver = null; } } catch (error) { console.error('Failed to serialize session:', error); } }); try { switch (operation) { case 'login': try { // Try to load existing session from input const existingSession = ((_b = (_a = items[0]) === null || _a === void 0 ? void 0 : _a.json) === null || _b === void 0 ? void 0 : _b.sessionData) || ''; if (existingSession) { await ig.state.deserialize(existingSession); returnData.push({ success: true, authenticated: true, userId: ig.state.cookieUserId, sessionData: existingSession, requiresTwoFactor: false, message: 'Loaded existing session', }); break; } // Normal login flow await ig.simulate.preLoginFlow(); const loggedInUser = await ig.account.login(credentials.username, credentials.password); // Post login flow in next tick process.nextTick(async () => await ig.simulate.postLoginFlow()); // Wait for session data to be captured sessionPromise = new Promise((resolve) => { sessionResolver = resolve; }); // Wait a short time for the session to be captured const loginSessionData = await Promise.race([ sessionPromise, new Promise((_, reject) => setTimeout(() => reject(new Error('Session capture timeout')), 5000)) ]).catch(() => null); // Store session data in credentials if (loginSessionData) { console.log('Session data captured. Please update your Instagram credentials with this session data.'); } returnData.push({ success: true, authenticated: true, userId: ig.state.cookieUserId, sessionData: loginSessionData, requiresTwoFactor: false, user: loggedInUser, }); } catch (error) { // Check if this is a 2FA required error const err = error; if ((_d = (_c = err.response) === null || _c === void 0 ? void 0 : _c.body) === null || _d === void 0 ? void 0 : _d.two_factor_info) { // Extract 2FA info const { username, totp_two_factor_on, two_factor_identifier } = err.response.body.two_factor_info; // Determine the default verification method based on what's enabled const defaultVerificationMethod = totp_two_factor_on ? '0' : '1'; returnData.push({ success: false, authenticated: false, requiresTwoFactor: true, username, twoFactorIdentifier: two_factor_identifier, totpEnabled: totp_two_factor_on, defaultVerificationMethod, message: `Two-factor authentication required. Please use the 'Submit 2FA Code' operation with the code received via ${defaultVerificationMethod === '1' ? 'SMS' : 'TOTP (Google Authenticator)'}`, }); } else { throw error; } } break; case 'submit2FA': const twoFactorCode = this.getNodeParameter('twoFactorCode', 0); const verificationMethod = this.getNodeParameter('verificationMethod', 0); const trustThisDevice = this.getNodeParameter('trustThisDevice', 0); // Get data from previous step or parameters let twoFactorIdentifier = this.getNodeParameter('twoFactorIdentifier', 0); let username = this.getNodeParameter('username', 0); // If not provided in parameters, try to get from input if (!twoFactorIdentifier || !username) { // Check if we have input data if (items.length > 0 && items[0].json) { twoFactorIdentifier = items[0].json.twoFactorIdentifier || ''; username = items[0].json.username || ''; } // If still not available, use credentials username if (!username) { username = credentials.username; } } // Validate required fields if (!twoFactorCode) { throw new Error('Two-factor code is required'); } if (!twoFactorIdentifier) { throw new Error('Two-factor identifier is required. Please connect this node to the output of a Login node that returned a 2FA requirement.'); } if (!username) { throw new Error('Username is required. Please connect this node to the output of a Login node that returned a 2FA requirement.'); } // Verify 2FA code const loggedInUser = await ig.account.twoFactorLogin({ username, verificationCode: twoFactorCode, twoFactorIdentifier, verificationMethod, trustThisDevice: trustThisDevice ? '1' : '0', }); // Post login flow in next tick process.nextTick(async () => await ig.simulate.postLoginFlow()); // Wait for session data to be captured sessionPromise = new Promise((resolve) => { sessionResolver = resolve; }); // Wait a short time for the session to be captured const twoFactorSessionData = await Promise.race([ sessionPromise, new Promise((_, reject) => setTimeout(() => reject(new Error('Session capture timeout')), 5000)) ]).catch(() => null); // Store session data in credentials if (twoFactorSessionData) { console.log('Session data captured. Please update your Instagram credentials with this session data.'); } returnData.push({ success: true, authenticated: true, userId: ig.state.cookieUserId, sessionData: twoFactorSessionData, requiresTwoFactor: false, user: loggedInUser, }); break; case 'loadSession': const savedSessionData = this.getNodeParameter('sessionData', 0); if (!savedSessionData) { throw new Error('Session data is required'); } await ig.state.deserialize(savedSessionData); // Store session data in credentials console.log('Session data captured. Please update your Instagram credentials with this session data.'); returnData.push({ success: true, authenticated: true, userId: ig.state.cookieUserId, sessionData: savedSessionData, message: 'Session loaded successfully', }); break; default: throw new Error(`The operation "${operation}" is not supported!`); } } catch (error) { if (this.continueOnFail()) { returnData.push({ success: false, error: error instanceof Error ? error.message : 'An unknown error occurred', }); } else { throw error; } } // Always return data, even if empty if (returnData.length === 0) { returnData.push({ success: false, message: 'No data returned from operation', }); } return [this.helpers.returnJsonArray(returnData)]; } } exports.InstagramAuth = InstagramAuth;