Skip to content

Instantly share code, notes, and snippets.

@Pitasi
Last active March 12, 2024 15:06
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Pitasi/574cb19348141d7bf8de83a0555fd2dc to your computer and use it in GitHub Desktop.
Save Pitasi/574cb19348141d7bf8de83a0555fd2dc to your computer and use it in GitHub Desktop.
Telegram website login widget, signature check sample using Node.js
// Copied by https://gist.github.com/dotcypress/8fd12d6e886cd74bba8f1aa8dbd346aa,
// thanks for improving code style
const { createHash, createHmac } = require('crypto');
const TOKEN = "ABC:12345...";
// I prefer get the secret's hash once but check the gist linked
// on line 1 if you prefer passing the bot token as a param
const secret = createHash('sha256')
.update(TOKEN)
.digest()
function checkSignature ({ hash, ...data }) {
const checkString = Object.keys(data)
.sort()
.filter((k) => data[k])
.map(k => (`${k}=${data[k]}`))
.join('\n');
const hmac = createHmac('sha256', secret)
.update(checkString)
.digest('hex');
return hmac === hash;
}
// Sample usage
const payload = {
id: '424242424242',
first_name: 'John',
last_name: 'Doe',
username: 'username',
photo_url: 'https://t.me/i/userpic/320/username.jpg',
auth_date: '1519400000',
hash: '87e5a7e644d0ee362334d92bc8ecc981ca11ffc11eca809505'
}
checkSignature(payload)
@rbeer
Copy link

rbeer commented Jan 15, 2022

Did you actually use this token const TOKEN = "ABC:12345..."; to create the hash in payload?
The example as it is doesn't work for me. It works as soon as I use real data, i.e. a live response from Telegram and my own bot token.
I think people merely copy/pasting and expecting this to work is what leads to confusion.

Replace payload.hash with fdd4901b22ee5ea34d77caeac88b5ecba818226ab9c7c7775c8ff675d7e4cb92 and this example works.

I think the gist you copied this from always used some made-up value for the hash, to begin with. Even the length doesn't add up.

@lub0v
Copy link

lub0v commented Nov 17, 2022

@ImTheDeveloper I had the same issue and found out that when users do not specify their last_name the hash does not match. To fix the error, I've filtered out empty data for checkString:

const checkString = Object.keys(data)
    .sort()
    .filter((k) => data[k])
    .map(k => (`${k}=${data[k]}`))
    .join('\n');

@ImTheDeveloper
Copy link

ImTheDeveloper commented Nov 17, 2022 via email

@Pitasi
Copy link
Author

Pitasi commented Nov 17, 2022

Thanks @lub0v, I updated the gist with your suggestion :)

@aslanorlov
Copy link

aslanorlov commented Mar 12, 2024

Updated version

const secret = crypto.createHash('sha256')
      .update(botToken?.trim())
      .digest();

const checkString = Object.keys(data)
    .sort()
    .filter((k) => data[k])
    .filter((k) => ![ 'hash' ].includes(k))
    .map(k => (`${k}=${data[k]}`))
    .join('\n');

const hmac = crypto.createHmac('sha256', secret)
    .update(checkString)
    .digest('hex');

return hmac === data.hash;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment