dilame / instagram-private-api

NodeJS Instagram private API SDK. Written in TypeScript.
MIT License
5.93k stars 1.14k forks source link

How to store user session in v1 #732

Closed nikitaulshin closed 5 years ago

nikitaulshin commented 5 years ago

Hello and thanks for that awesome library!

I'm building a simple chat bot software. The application performs some operations in the background.

Is it possible to store user session somehow for my REST API (preferably in the database) so the application will not need to perform the instagram login flow every time?

PerlBug commented 5 years ago

id like to know this as well

dilame commented 5 years ago

https://github.com/dilame/instagram-private-api/blob/master/examples/session.example.ts

alexgiul commented 5 years ago

@dilame does it mean the new version, everytime i call the login, it does the entire login workflow? If I save the cookie & device objects into the database how can I avoid the login? I am experiencing a strange issue: IG this morning asked me to login into the app and I was thinking if it could be related to the fact that I am doing login everytime I execute some automations

nikitaulshin commented 5 years ago

In addition you should save device data. Explore it in core/state.ts

Which device data should I save? I can see a lot of fields in the generateDevice method. And how I should restore it? Should I just reassign proper fields?

nikitaulshin commented 5 years ago

@alexgiul I think you should call

// In order to restore session cookies you need this await ig.state.deserializeCookieJar(JSON.stringify(cookies));

with your serialized cookie string.

Thus you'll bypass extra login flows.

dilame commented 5 years ago

@dilame does it mean the new version, everytime i call the login, it does the entire login workflow? If I save the cookie & device objects into the database how can I avoid the login? I am experiencing a strange issue: IG this morning asked me to login into the app and I was thinking if it could be related to the fact that I am doing login everytime I execute some automations

You can avoid the login by just restoring ig.state. You only need to login once and save all the needed data in your persistent storage.

dilame commented 5 years ago

In addition you should save device data. Explore it in core/state.ts

Which device data should I save? I can see a lot of fields in the generateDevice method. And how I should restore it? Should I just reassign proper fields?

It is your choice what fields to save, but i recommend at least save all the fields that generateDevice generates. To restore it you have to just reassign it.

When you are restoring it you don't have to call generateDevice()

BTW will be good if someone could help me with docs, i don't have enough time for everything

gabrielstuff commented 5 years ago

Hello,

I'm trying to migrate few cli tools I used to have with pre-1.0 version. Lots of change but much cleaner functions ! Thanks for this work.

So If I understand correctly we can deserialize the cookie and load it. As for the other state infos, I guess we should simply save the infos generated by generateDevice function : https://github.com/dilame/instagram-private-api/blob/master/src/core/state.ts#L175

This means:

{
  deviceString
  deviceId
  uuid
  phoneId
  adid
  build
}

Should be saved and restored. Let's try then !

gabrielstuff commented 5 years ago

Ok, here is my little functionnal code for those who'd like to login once and then use the device + cookie :

;(async () => {
  const ig = new IgApiClient()
  ig.state.generateDevice(config.auth.user)
  // ig.state.device
  try {
    if (fs.existsSync(`${userCookiePath}`) && fs.existsSync(`${userDevicePath}`)) {
      console.log('loading device and session from disk...')
      let savedCookie = fs.readFileSync(userCookiePath, 'utf-8')
      let savedDevice = JSON.parse(fs.readFileSync(userDevicePath), 'utf-8')
      await ig.state.deserializeCookieJar(savedCookie)
      ig.state.deviceString = savedDevice.deviceString
      ig.state.deviceId = savedDevice.deviceId
      ig.state.uuid = savedDevice.uuid
      ig.state.adid = savedDevice.adid
      ig.state.build = savedDevice.build
      let pk = await ig.user.getIdByUsername(config.auth.user)
      await listFollowers(pk)
    } else {
      Bluebird.try(async () => {
        const auth = await ig.account.login(config.auth.user, config.auth.password)
        const cookieJar = await ig.state.serializeCookieJar()
        fs.writeFileSync(userCookiePath, JSON.stringify(cookieJar), 'utf-8')
        let device = (({ deviceString, deviceId, uuid, adid, build }) => ({ deviceString, deviceId, uuid, adid, build }))(ig.state)
        fs.writeFileSync(userDevicePath, JSON.stringify(device), 'utf-8')
        await listFollowers(auth.pk)
      }).catch(IgCheckpointError, async () => {
        console.log(ig.state.checkpoint) // Checkpoint info here
        await ig.challenge.auto(true) // Requesting sms-code or click "It was me" button
        console.log(ig.state.checkpoint) // Challenge info here
        const { code } = await inquirer.prompt([
          {
            type: 'input',
            name: 'code',
            message: 'Enter code'
          }
        ])
        await ig.challenge.sendSecurityCode(code)
        await ig.account.login(config.auth.user, config.auth.password)
      })
    }
  } catch (err) {
    console.error(err)
  }
})()

So far it works !

gabrielstuff commented 5 years ago

I'm not really happy with device loading part. I think we should have a loadDevice method and maybe better / easier a loadState() method. What do you think @dilame

dilame commented 5 years ago

I'm not really happy with device loading part. I think we should have a loadDevice method and maybe better / easier a loadState() method. What do you think @dilame

It would be nice if we could write loadDevice or loadState methods that would satisfy everyone's requirement. I wanted to implement it, but stucked into too much variations. If you can propose something good - you are welcome. But, please, remember that your use-case is totally different from my use-case, so the proposal must be flexible enough to satisfy both.

iamthewalkingkode commented 5 years ago

Ok, here is my little functionnal code for those who'd like to login once and then use the device + cookie :

;(async () => {
  const ig = new IgApiClient()
  ig.state.generateDevice(config.auth.user)
  // ig.state.device
  try {
    if (fs.existsSync(`${userCookiePath}`) && fs.existsSync(`${userDevicePath}`)) {
      console.log('loading device and session from disk...')
      let savedCookie = fs.readFileSync(userCookiePath, 'utf-8')
      let savedDevice = JSON.parse(fs.readFileSync(userDevicePath), 'utf-8')
      await ig.state.deserializeCookieJar(savedCookie)
      ig.state.deviceString = savedDevice.deviceString
      ig.state.deviceId = savedDevice.deviceId
      ig.state.uuid = savedDevice.uuid
      ig.state.adid = savedDevice.adid
      ig.state.build = savedDevice.build
      let pk = await ig.user.getIdByUsername(config.auth.user)
      await listFollowers(pk)
    } else {
      Bluebird.try(async () => {
        const auth = await ig.account.login(config.auth.user, config.auth.password)
        const cookieJar = await ig.state.serializeCookieJar()
        fs.writeFileSync(userCookiePath, JSON.stringify(cookieJar), 'utf-8')
        let device = (({ deviceString, deviceId, uuid, adid, build }) => ({ deviceString, deviceId, uuid, adid, build }))(ig.state)
        fs.writeFileSync(userDevicePath, JSON.stringify(device), 'utf-8')
        await listFollowers(auth.pk)
      }).catch(IgCheckpointError, async () => {
        console.log(ig.state.checkpoint) // Checkpoint info here
        await ig.challenge.auto(true) // Requesting sms-code or click "It was me" button
        console.log(ig.state.checkpoint) // Challenge info here
        const { code } = await inquirer.prompt([
          {
            type: 'input',
            name: 'code',
            message: 'Enter code'
          }
        ])
        await ig.challenge.sendSecurityCode(code)
        await ig.account.login(config.auth.user, config.auth.password)
      })
    }
  } catch (err) {
    console.error(err)
  }
})()

So far it works !

It works for you but I still get "requires login" error 😢😭😭

leafdropco commented 5 years ago

I'm experiencing the same thing, did anyone get a working example?

how2945ard commented 4 years ago

experiencing the requires login error too

gabrielstuff commented 4 years ago

Everyone has a different need. What does not "work". As stated by @dilame my needs with loadstate is different from his and probably from yours. Did you tried : https://github.com/dilame/instagram-private-api/issues/732#issuecomment-498624236

jcagz96 commented 4 years ago

i'm having the following problem : IgLoginRequiredError: GET /api/v1/users/search/?timezone_offset=3600&q=cristiano&count=30 - 403 Forbidden; login_required

Nerixyz commented 4 years ago

Reset your session

ZefianAlfian commented 2 years ago

Ok, here is my little functionnal code for those who'd like to login once and then use the device + cookie :

;(async () => {
  const ig = new IgApiClient()
  ig.state.generateDevice(config.auth.user)
  // ig.state.device
  try {
    if (fs.existsSync(`${userCookiePath}`) && fs.existsSync(`${userDevicePath}`)) {
      console.log('loading device and session from disk...')
      let savedCookie = fs.readFileSync(userCookiePath, 'utf-8')
      let savedDevice = JSON.parse(fs.readFileSync(userDevicePath), 'utf-8')
      await ig.state.deserializeCookieJar(savedCookie)
      ig.state.deviceString = savedDevice.deviceString
      ig.state.deviceId = savedDevice.deviceId
      ig.state.uuid = savedDevice.uuid
      ig.state.adid = savedDevice.adid
      ig.state.build = savedDevice.build
      let pk = await ig.user.getIdByUsername(config.auth.user)
      await listFollowers(pk)
    } else {
      Bluebird.try(async () => {
        const auth = await ig.account.login(config.auth.user, config.auth.password)
        const cookieJar = await ig.state.serializeCookieJar()
        fs.writeFileSync(userCookiePath, JSON.stringify(cookieJar), 'utf-8')
        let device = (({ deviceString, deviceId, uuid, adid, build }) => ({ deviceString, deviceId, uuid, adid, build }))(ig.state)
        fs.writeFileSync(userDevicePath, JSON.stringify(device), 'utf-8')
        await listFollowers(auth.pk)
      }).catch(IgCheckpointError, async () => {
        console.log(ig.state.checkpoint) // Checkpoint info here
        await ig.challenge.auto(true) // Requesting sms-code or click "It was me" button
        console.log(ig.state.checkpoint) // Challenge info here
        const { code } = await inquirer.prompt([
          {
            type: 'input',
            name: 'code',
            message: 'Enter code'
          }
        ])
        await ig.challenge.sendSecurityCode(code)
        await ig.account.login(config.auth.user, config.auth.password)
      })
    }
  } catch (err) {
    console.error(err)
  }
})()

So far it works !

it works thanks for your help