gram-js / gramjs

NodeJS/Browser MTProto API Telegram client library,
MIT License
1.28k stars 179 forks source link

Qr code login #151

Closed Tomcat-42 closed 3 years ago

Tomcat-42 commented 3 years ago

First of all, awesome works guys!

I'm trying to login via qr code, so, reading the docs I generated a login token via:

    ...
    const client = new TelegramClient(stringSession, Number(apiId), apiHash, {
      connectionRetries: 5,
    });

    await client.connect();

    const loginToken: Api.auth.TypeLoginToken = await client.invoke(
      new Api.auth.ExportLoginToken({
        apiId: Number(apiId),
        apiHash,
        exceptIds: [9505834],
      }),
    );
    if (!(loginToken instanceof Api.auth.LoginToken)) {
      return;
    }
    console.log(`tg://login?token=${loginToken.token.toString('base64url')}`);
    ...

After that, I scanned the returned url in my phone. So, how can I start the client using this token? what is the correct use of client.start(<AuthUser>)? The AuthuUser interface impose phoneCode use, but I want to login only via qr code.

Thanks for the help.

painor commented 3 years ago

There is already a helper method for QR codes. signInUserWithQrCode. you can take a look on how it implements it.

Basically after you scan the code telegram would send you an event. after which you need to call ExportLoginToken again.

Tomcat-42 commented 3 years ago

Ok, thanks for the help. I used client.signInUserWithQrCode:

    try {
      const client = new TelegramClient(stringSession, Number(apiId), apiHash, {
        connectionRetries: 5,
      });

      await client.connect();

      await client.signInUserWithQrCode(
        { apiId: Number(apiId), apiHash },
        {
          phoneNumber: <MY_NUMBER>,
          password: async () => undefined,
          phoneCode: async () => undefined,
          onError: (err) => console.log(`LOGIN ERROR => ${err}`),
          qrCode: async (qrCode) => {
             console.log(qrCode.token, qrCode.expires);
          },
        },
      );
    } catch (err) {
      console.log(`ERROR => ${err}}`);
    }

But i got this error: CastError: Found wrong type for apiHash. expected string but received undefined.If you think this is a mistake please report it.. apiId and apiHash are both correct. Any clue of what is causing this? Thanks in advance.

painor commented 3 years ago

oh yeah that function wasn't mean for public usage. I only mentioned it so you can look at how it works.

It's trying to get apiId and apiHash from an env variable called process.env.TELEGRAM_T_API_ID. you can copy paste it's code into your own function and change those with the ones you are using.

Tomcat-42 commented 3 years ago

Ok then. I used the code in signInUserWithQrCode, generated the qr code and scanned it with my phone. But when the authentication is approved on my phone an error is raised:

RPCError: 401: AUTH_KEY_UNREGISTERED (caused by users.GetUsers)
...
 at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  code: 401,
  errorMessage: 'AUTH_KEY_UNREGISTERED'
...
Error: Could not get me
...

I think the updatePromise is responsible for this error:

    const updatePromise = new Promise((resolve) => {
      client.addEventHandler((update: Api.TypeUpdate) => {
        if (update instanceof Api.UpdateLoginToken) {
          resolve(undefined);
        }
      });
    });

This event handler is for when Telegram sends the auth update right? Any clue how to solve this error? I really liked gramjs and I hope I'm not being inconvenient.

painor commented 3 years ago

I'll update the code with an example of signing in with a QR code and make that function available for public use.

I will be using DC4 but it should work on any DCs.

Tomcat-42 commented 3 years ago

Ok, thanks dude. I'm looking forward for it!

painor commented 3 years ago

Should be fixed in latest patch. There was actually a piece of code in the library that made qr login broken :/ I've added an example in the docs as well.

    const user = await client.signInUserWithQrCode({ apiId, apiHash },
        {
            onError: async function(p1: Error) {
                console.log("error", p1);
                // true = stop the authentication processes
                return true;
            },
            qrCode: async (code) => {
                console.log("Convert the next string to a QR code and scan it");
                console.log(
                    `tg://login?token=${code.token.toString("base64url")}`
                );
            },
            password: async (hint) => {
                // password if needed
                return "1111";
            }
        }
    );
    console.log("user is", user);
Tomcat-42 commented 3 years ago

Thanks! Worked like a charm.