alik0211 / mtproto-core

Telegram API JS (MTProto) client library for Node.js and browser
https://mtproto-core.js.org
GNU General Public License v3.0
633 stars 118 forks source link

How to authenticate? #177

Open Argon007 opened 3 years ago

Argon007 commented 3 years ago

Hello,

Fairly new to Node and Telegram so this is a bit overwhelming for me. I've created an "api.js" file and copied the content of your api.js file into it (my api and api_hash are configured in the file).

Next i've created the "example.js" file like this:

var api = require('./api.js');

(async () => {
    const user = await getUser();

    const phone = '+3xxxxxxxx';
    const code = 'XXXXX';

    if (!user) {
      const { phone_code_hash } = await sendCode(phone);

      try {
        const signInResult = await signIn({
          code,
          phone,
          phone_code_hash,
        });

        if (signInResult._ === 'auth.authorizationSignUpRequired') {
          await signUp({
            phone,
            phone_code_hash,
          });
        }
      } catch (error) {
        if (error.error_message !== 'SESSION_PASSWORD_NEEDED') {
          console.log(`error:`, error);

          return;
        }

        // 2FA

        const password = 'MY_PERSONAL_2FA_CODE';

        const { srp_id, current_algo, srp_B } = await getPassword();
        const { g, p, salt1, salt2 } = current_algo;

        const { A, M1 } = await api.mtproto.crypto.getSRPParams({
          g,
          p,
          salt1,
          salt2,
          gB: srp_B,
          password,
        });

        const checkPasswordResult = await checkPassword({ srp_id, A, M1 });
      }
    }
  })();

async function getUser() {
    try {
      const user = await api.call('users.getFullUser', {
        id: {
          _: 'inputUserSelf',
        },
      });

      return user;
    } catch (error) {
      return null;
    }
  }

  function sendCode(phone) {
    return api.call('auth.sendCode', {
      phone_number: phone,
      settings: {
        _: 'codeSettings',
      },
    });
  }

  function signIn({ code, phone, phone_code_hash }) {
    return api.call('auth.signIn', {
      phone_code: code,
      phone_number: phone,
      phone_code_hash: phone_code_hash,
    });
  }

  function signUp({ phone, phone_code_hash }) {
    return api.call('auth.signUp', {
      phone_number: phone,
      phone_code_hash: phone_code_hash,
      first_name: 'MTProto',
      last_name: 'Core',
    });
  }

  function getPassword() {
    return api.call('account.getPassword');
  }

  function checkPassword({ srp_id, A, M1 }) {
    return api.call('auth.checkPassword', {
      password: {
        _: 'inputCheckPasswordSRP',
        srp_id,
        A,
        M1,
      },
    });
  }

So I entered my phone number, but can't write anything yet for the "code" variable since I didn't receive the code yet. I also changed the 2FA code to my personal 2FA string. Once I start example.js I get:

users.getFullUser error: { _: 'mt_rpc_error', error_code: 401, error_message: 'SESSION_PASSWORDNEEDED' } auth.sendCode error: { : 'mt_rpc_error', error_code: 500, error_message: 'AUTH_RESTART' } (node:3624408) UnhandledPromiseRejectionWarning: # (Use node --trace-warnings ... to show where the warning was created) (node:3624408) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3) (node:3624408) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code

Can anyone point me in the right direction how to get authenticated?

Thanks!

Argon007 commented 3 years ago

hmm, maybe i'm getting closer now. I've executed this:

function sendCode(phone) {
  return api.call('auth.sendCode', {
    phone_number: phone,
    settings: {
      _: 'codeSettings',
    },
  });
}

and got the code from Telegram. Next I want to execute this:

function signIn({ code, phone, phone_code_hash }) {
  return api.call('auth.signIn', {
    phone_code: code,
    phone_number: phone,
    phone_code_hash: phone_code_hash,
  });
}

but I don't know what the "phone_code_hash" needs to be? I have the TG code and my phone number of course, but not the "phone_code_hash"...

atiquefiroz commented 3 years ago

hmm, maybe i'm getting closer now. I've executed this:

function sendCode(phone) {
  return api.call('auth.sendCode', {
    phone_number: phone,
    settings: {
      _: 'codeSettings',
    },
  });
}

and got the code from Telegram. Next I want to execute this:

function signIn({ code, phone, phone_code_hash }) {
  return api.call('auth.signIn', {
    phone_code: code,
    phone_number: phone,
    phone_code_hash: phone_code_hash,
  });
}

but I don't know what the "phone_code_hash" needs to be? I have the TG code and my phone number of course, but not the "phone_code_hash"...

you will get the phone_code_hash from the API call to your sendCode method. const res = await sendCode('9999XXXXXXX'); const phone_code_hash = res['phone_code_hash'];

Which you can use in the later API call. I hope that helps.

Argon007 commented 3 years ago

Sorry sir, I still have troubles authenticating against Telegram. Is there someone over here that can show me the exact way how they achieved it?

If I execute the script I receive the Telegram message with an unique code, but can't enter the code into the script since it's already running. So I need to stop the script, enter the "code" I received but I still get an error if I do it that way...

users.getFullUser error: {
  _: 'mt_rpc_error',
  error_code: 401,
  error_message: 'AUTH_KEY_UNREGISTERED'
}
auth.signIn error: {
  _: 'mt_rpc_error',
  error_code: 401,
  error_message: 'SESSION_PASSWORD_NEEDED'
}

Thanks again!

adgamerx commented 3 years ago

Hey, did you authenticated your app??

Argon007 commented 3 years ago

I can't... that's the problem :).

I'm adding my phone number to the script, the api_id and api_hash and run it. Once I run it I get the code from Telegram.

I stop the script and add the code into the script and run it again. Next I get are the errors described here above.

So I wonder how you did the authentication process. (friendly reminder, i'm fairly new to Node, so i'm not a daily coder ;-) )

Thanks!

Argon007 commented 3 years ago

No one that can help me?

andrascodes commented 3 years ago

This worked for me with my own phone number but it doesn't work with the test settings:

// main.js
const MTProto = require("@mtproto/core");
const prompt = require("prompt");

const API = require("./telegramApi");

const api = new API();

async function main() {
  const user = await api.getUser();
  console.log("User 1st try: ", user);

  //   const phone = "+99966XYYYY";
  //   const code = "XXXXX";

  if (!user) {
    const { phone } = await prompt.get("phone");
    const { phone_code_hash } = await api.sendCode(phone);

    const { code } = await prompt.get("code");
    try {
      const signInResult = await api.signIn({
        code,
        phone,
        phone_code_hash,
      });
      console.log(signInResult);

      if (signInResult._ === "auth.authorizationSignUpRequired") {
        const singUpResult = await api.signUp({
          phone,
          phone_code_hash,
        });
        console.log(singUpResult);
      }

      const newUser = await api.getUser();
      console.log("User 2nd try: ", newUser);
    } catch (error) {
      if (error.error_message !== "SESSION_PASSWORD_NEEDED") {
        console.log(`error:`, error);

        return;
      }

      // 2FA...
    }
  }

  const result = await api.call("help.getNearestDc");
  console.log(result);
  process.exit(0);
}

main();
// api.js
const path = require("path");
const MTProto = require("@mtproto/core");
const { sleep } = require("@mtproto/core/src/utils/common");

class API {
  constructor({ test } = { test: false }) {
    this.mtproto = new MTProto({
      api_id: "",
      api_hash: "",
      test,
      storageOptions: {
        path: path.resolve(__dirname, "./telegramData.json"),
      },
    });
  }

  async call(method, params, options = {}) {
    console.log(method);
    try {
      const result = await this.mtproto.call(method, params, options);

      console.log(result);
      return result;
    } catch (error) {
      console.error(error);
      const { error_code, error_message } = error;

      if (error_code === 420) {
        const seconds = Number(error_message.split("FLOOD_WAIT_")[1]);
        const ms = seconds * 1000;

        await sleep(ms);

        return this.call(method, params, options);
      }

      if (error_code === 303) {
        const [type, dcIdAsString] = error_message.split("_MIGRATE_");

        const dcId = Number(dcIdAsString);

        // If auth.sendCode call on incorrect DC need change default DC, because
        // call auth.signIn on incorrect DC return PHONE_CODE_EXPIRED error
        if (type === "PHONE") {
          await this.mtproto.setDefaultDc(dcId);
        } else {
          Object.assign(options, { dcId });
        }

        return this.call(method, params, options);
      }

      return Promise.reject(error);
    }
  }

  async getUser() {
    try {
      const user = await this.call("users.getFullUser", {
        id: {
          _: "inputUserSelf",
        },
      });

      return user;
    } catch (error) {
      return null;
    }
  }

  sendCode(phone) {
    try {
      return this.call("auth.sendCode", {
        phone_number: phone,
        settings: {
          _: "codeSettings",
        },
      });
    } catch (error) {
      throw error;
    }
  }

  signIn({ code, phone, phone_code_hash }) {
    return this.call("auth.signIn", {
      phone_code: code,
      phone_number: phone,
      phone_code_hash: phone_code_hash,
    });
  }

  signUp({ phone, phone_code_hash }) {
    return this.call("auth.signUp", {
      phone_number: phone,
      phone_code_hash: phone_code_hash,
      first_name: "MTProto",
      last_name: "Core",
    });
  }

  getPassword() {
    return this.call("account.getPassword");
  }

  checkPassword({ srp_id, A, M1 }) {
    return this.call("auth.checkPassword", {
      password: {
        _: "inputCheckPasswordSRP",
        srp_id,
        A,
        M1,
      },
    });
  }
}

module.exports = API;

Install the prompt library, so you can type in your phone and code:

npm i prompt

I didn't use the 2FA section, so you need to add that yourself.

allandiego commented 3 years ago

@andrewszucs what error do you get with test number? I was having problems because docs says that verification code are 5 digits but in fact It was 6 to get my testing number work

andrascodes commented 3 years ago

@andrewszucs what erro u get with test number? I was having problems because docs says that verification code are 5 digits but in fact It was 6 to get my testing number work

That was my problem too. Thanks a lot @allandiego ! It works now. 🙌

Argon007 commented 3 years ago

This worked for me with my own phone number but it doesn't work with the test settings:

Install the prompt library, so you can type in your phone and code:

npm i prompt

I didn't use the 2FA section, so you need to add that yourself.

Hi, I got this error now:

internal/modules/cjs/loader.js:883 throw err; ^

Error: Cannot find module './telegramApi'

Which dependency is this?

Thanks!

erdemozor commented 3 years ago

This worked for me with my own phone number but it doesn't work with the test settings:

// main.js
const MTProto = require("@mtproto/core");
const prompt = require("prompt");

const API = require("./telegramApi");

const api = new API();

async function main() {
  const user = await api.getUser();
  console.log("User 1st try: ", user);

  //   const phone = "+99966XYYYY";
  //   const code = "XXXXX";

  if (!user) {
    const { phone } = await prompt.get("phone");
    const { phone_code_hash } = await api.sendCode(phone);

    const { code } = await prompt.get("code");
    try {
      const signInResult = await api.signIn({
        code,
        phone,
        phone_code_hash,
      });
      console.log(signInResult);

      if (signInResult._ === "auth.authorizationSignUpRequired") {
        const singUpResult = await api.signUp({
          phone,
          phone_code_hash,
        });
        console.log(singUpResult);
      }

      const newUser = await api.getUser();
      console.log("User 2nd try: ", newUser);
    } catch (error) {
      if (error.error_message !== "SESSION_PASSWORD_NEEDED") {
        console.log(`error:`, error);

        return;
      }

      // 2FA...
    }
  }

  const result = await api.call("help.getNearestDc");
  console.log(result);
  process.exit(0);
}

main();
// api.js
const path = require("path");
const MTProto = require("@mtproto/core");
const { sleep } = require("@mtproto/core/src/utils/common");

class API {
  constructor({ test } = { test: false }) {
    this.mtproto = new MTProto({
      api_id: "",
      api_hash: "",
      test,
      storageOptions: {
        path: path.resolve(__dirname, "./telegramData.json"),
      },
    });
  }

  async call(method, params, options = {}) {
    console.log(method);
    try {
      const result = await this.mtproto.call(method, params, options);

      console.log(result);
      return result;
    } catch (error) {
      console.error(error);
      const { error_code, error_message } = error;

      if (error_code === 420) {
        const seconds = Number(error_message.split("FLOOD_WAIT_")[1]);
        const ms = seconds * 1000;

        await sleep(ms);

        return this.call(method, params, options);
      }

      if (error_code === 303) {
        const [type, dcIdAsString] = error_message.split("_MIGRATE_");

        const dcId = Number(dcIdAsString);

        // If auth.sendCode call on incorrect DC need change default DC, because
        // call auth.signIn on incorrect DC return PHONE_CODE_EXPIRED error
        if (type === "PHONE") {
          await this.mtproto.setDefaultDc(dcId);
        } else {
          Object.assign(options, { dcId });
        }

        return this.call(method, params, options);
      }

      return Promise.reject(error);
    }
  }

  async getUser() {
    try {
      const user = await this.call("users.getFullUser", {
        id: {
          _: "inputUserSelf",
        },
      });

      return user;
    } catch (error) {
      return null;
    }
  }

  sendCode(phone) {
    try {
      return this.call("auth.sendCode", {
        phone_number: phone,
        settings: {
          _: "codeSettings",
        },
      });
    } catch (error) {
      throw error;
    }
  }

  signIn({ code, phone, phone_code_hash }) {
    return this.call("auth.signIn", {
      phone_code: code,
      phone_number: phone,
      phone_code_hash: phone_code_hash,
    });
  }

  signUp({ phone, phone_code_hash }) {
    return this.call("auth.signUp", {
      phone_number: phone,
      phone_code_hash: phone_code_hash,
      first_name: "MTProto",
      last_name: "Core",
    });
  }

  getPassword() {
    return this.call("account.getPassword");
  }

  checkPassword({ srp_id, A, M1 }) {
    return this.call("auth.checkPassword", {
      password: {
        _: "inputCheckPasswordSRP",
        srp_id,
        A,
        M1,
      },
    });
  }
}

module.exports = API;

Install the prompt library, so you can type in your phone and code:

npm i prompt

I didn't use the 2FA section, so you need to add that yourself.

Adding a prompt for the SMS verification works. This should really be added to the docs here.

mamiu commented 2 years ago

@alik0211 Can we add this please to the docs? I'm willing to help.

vmail commented 2 years ago

Does anyone else have an issue with the phone code? I have to keep entering the phone code every time I start node. The data JSON file is being written to, but it does looks like it is being read on start

IgorKurkov commented 2 years ago

Finally, deal with it. Official docs btw are better than all this code above. Thank you @alik0211 !

*** HOW TO LOGIN:

  • set api_id in api.js file from telegram application
  • set api_hash in api.js file
  • log in to the account node auth.js
  • prompt phone & prompt auth code given from tg channel of telegram official
  • see the updates. successful login.

// API.js
/* eslint-disable no-undef */
const path = require("path");
const MTProto = require("@mtproto/core");
const { sleep } = require("@mtproto/core/src/utils/common");

class API {
  constructor() {
    this.mtproto = new MTProto({
      // lucky
      api_id: api_id,
      api_hash: api_hash,

      storageOptions: {
        path: path.resolve(__dirname, "./1.json"),
      },
    });
  }

  async call(method, params, options = {}) {
    try {
      const result = await this.mtproto.call(method, params, options);

      return result;
    } catch (error) {
      console.log(`${method} error:`, error);

      const { error_code, error_message } = error;

      if (error_code === 420) {
        const seconds = Number(error_message.split("FLOOD_WAIT_")[1]);
        const ms = seconds * 1000;

        await sleep(ms);

        return this.call(method, params, options);
      }

      if (error_code === 303) {
        const [type, dcIdAsString] = error_message.split("_MIGRATE_");

        const dcId = Number(dcIdAsString);

        // If auth.sendCode call on incorrect DC need change default DC, because
        // call auth.signIn on incorrect DC return PHONE_CODE_EXPIRED error
        if (type === "PHONE") {
          await this.mtproto.setDefaultDc(dcId);
        } else {
          Object.assign(options, { dcId });
        }

        return this.call(method, params, options);
      }

      return Promise.reject(error);
    }
  }
}

const api = new API();

module.exports = api;

auth.js

// AUTH.js
/* eslint-disable no-undef */
const api = require("./api-origin");
const prompt = require("prompt");
const { sendMessage } = require("./updates");

async function getUser() {
  try {
    const user = await api.call("users.getFullUser", {
      id: {
        _: "inputUserSelf",
      },
    });

    return user;
  } catch (error) {
    return null;
  }
}

function signIn({ code, phone, phone_code_hash }) {
  return api
    .call("auth.signIn", {
      phone_code: code,
      phone_number: phone,
      phone_code_hash: phone_code_hash,
    })
    .then((v) => {
      console.log("LOGIN SUCCESS: ", v);
      sendMessage();
    })
    .catch((e) => console.log("LOGIN FAIL: ", e));
}

function sendCode(phone) {
  return api.call("auth.sendCode", {
    phone_number: phone,
    settings: {
      _: "codeSettings",
    },
  });
}

(async () => {
  const user = await getUser();

  // const phone = "+9XXXXXXXXXX";

  if (!user) {
    const { phone } = await prompt.get("phone");
    const { phone_code_hash } = await sendCode(phone);

    const { code } = await prompt.get("code");
    try {
      const signInResult = await signIn({
        code,
        phone,
        phone_code_hash,
      });

      if (signInResult._ === "auth.authorizationSignUpRequired") {
        await signUp({
          phone,
          phone_code_hash,
        });
      }
    } catch (error) {
      if (error.error_message !== "SESSION_PASSWORD_NEEDED") {
        console.log(`error:`, error);

        return;
      }

      // 2FA

      const password = "USER_PASSWORD";

      const { srp_id, current_algo, srp_B } = await getPassword();
      const { g, p, salt1, salt2 } = current_algo;

      const { A, M1 } = await api.mtproto.crypto.getSRPParams({
        g,
        p,
        salt1,
        salt2,
        gB: srp_B,
        password,
      });

      const checkPasswordResult = await checkPassword({ srp_id, A, M1 });
    }
  }
})();

updates.js

// UPDATES.js

/* eslint-disable no-undef */
const api = require("./api-origin");

api.mtproto.updates.on("updates", (updateInfo) => {
  console.log("updates:", updateInfo);
});

function sendMessage() {
  api.call("messages.sendMessage", {
    clear_draft: true,

    peer: {
      _: "inputPeerSelf",
    },
    message: "Hello @mtproto_core",
    entities: [
      {
        _: "messageEntityBold",
        offset: 6,
        length: 13,
      },
    ],

    random_id:
      Math.ceil(Math.random() * 0xffffff) + Math.ceil(Math.random() * 0xffffff),
  });
}

module.exports = { sendMessage };
VityaSchel commented 1 year ago

@IgorKurkov

I made a smaller version of your script here

/* Example how to create MTProto:
const api = new MTProto({
  api_id: Number(process.env.APP_ID),
  api_hash: process.env.APP_HASH,

  storageOptions: { path: './tempdata.json' }
})
*/
// Inject right after creating MTProto
api.__call = api.call
api.call = async (name, params, options) => {
  if(options?.dcId) return api.__call(name, params, options)
  if(api.__defaultDc) {
    if(!options) options = {}
    options.dcId = api.__defaultDc
  }
  try {
    return await api.__call(name, params, options)
  } catch(e) {
    const migrateRegex = /_MIGRATE_(\d+)$/
    if(migrateRegex.test(e?.error_message)) {
      api.__defaultDc = Number(e.error_message.match(migrateRegex)[1])
      return await api.call(name, params, options)
    } else throw e
  }
}
0xCryptris commented 1 year ago

Did anybody get 2FA working? To me it seems the script already fails to recognize "SESSION_PASSWORD_NEEDED" but even if this gets bypassed to reach the 2FA section, I end up with error_code: 400, error_message: 'PASSWORD_HASH_INVALID'

VityaSchel commented 1 year ago

Did anybody get 2FA working? To me it seems the script already fails to recognize "SESSION_PASSWORD_NEEDED" but even if this gets bypassed to reach the 2FA section, I end up with error_code: 400, error_message: 'PASSWORD_HASH_INVALID'

See https://www.npmjs.com/package/telegram

tiulinh commented 1 year ago

i have a error: Please help me! ( const phone = '+99966XYYYY'; const code = 'XXXXXX' )

users.getFullUser error: { _: 'mt_rpc_error', error_code: 401, error_message: 'AUTH_KEYUNREGISTERED' } auth.sendCode error: { : 'mt_rpc_error', error_code: 400, error_message: 'PHONE_NUMBER_INVALID' }