Skip to content

Instantly share code, notes, and snippets.

@MarvinMiles
Created February 10, 2022 13:58
Show Gist options
  • Save MarvinMiles/f041205d872b0d8547d054eafeafe2a5 to your computer and use it in GitHub Desktop.
Save MarvinMiles/f041205d872b0d8547d054eafeafe2a5 to your computer and use it in GitHub Desktop.
Telegram user authentication in JavaScript via Web Crypto API (dependency-free)
async function validate(data, bot_token) {
const encoder = new TextEncoder()
const checkString = await Object.keys(data)
.filter((key) => key !== "hash")
.map((key) => `${key}=${data[key]}`)
.sort()
.join("\n")
//console.log('computed string:', checkString)
const tokenKey = await crypto.subtle.digest('SHA-256', encoder.encode(bot_token))
const secretKey = await crypto.subtle.importKey("raw", tokenKey, {name: "HMAC", hash: "SHA-256"}, true, ["sign"])
const signature = await crypto.subtle.sign("HMAC", secretKey, encoder.encode(checkString))
const hex = [...new Uint8Array(signature)].map(b => b.toString(16).padStart(2, '0')).join('')
//console.log('original hash:', data.hash)
//console.log('computed hash:', hex)
return data.hash === hex
}
@Dostonbek121
Copy link

what is this?

@MarvinMiles
Copy link
Author

MarvinMiles commented Feb 10, 2022

@Dostonbek121 this is most important part of https://gist.github.com/anonymous/6516521b1fb3b464534fbc30ea3573c2 but written in pure JS.

@fkuril
Copy link

fkuril commented May 21, 2022

Thank you @MarvinMiles, this actually worked!
I've used

const hmac = createHmac('sha256', secretKey)
  .update(dataCheckString)
  .digest('hex');

initially and couldn't understand what's wrong. Most of the examples online use the same approach which doesn't seem to be valid anymore.

@abc-1211
Copy link

@MarvinMiles Thanks for your code, it works perfect, but I found that due to the crypto.subtle, this function must be placed at the frontend webpage as this requires HTTPS to work. However, inside the function stated above, you will need to provide your bot_token. Saving credentials at the frontend may not be a safe approach. I tried to move it to the backend, but it will pops out an Error message. Cannot read property 'digest' of undefined. Do you got any other work around which it can prevent the bot_token leak?

@abc-1211
Copy link

abc-1211 commented Sep 25, 2022

For those who faced the same problem with me, this is the code in JS

const crypto = require("crypto");

function validate(data, token) {
  const secretKey = crypto.createHash("sha256").update(token).digest();
  const data_check_string = Object.keys(message)
    .filter((key) => key !== "hash")
    .map((key) => `${key}=${message[key]}`)
    .sort()
    .join("\n");
  const check_hash = crypto
    .createHmac("sha256", secretKey)
    .update(data_check_string)
    .digest("hex");

  return check_hash == data.hash;
}

Provided By Link

@geniustonya
Copy link

@MarvinMiles thanks a lot
@abc-1211 thanks , I had the same problem as you, recommended to replace "message" with "data"

@Solexki
Copy link

Solexki commented Dec 11, 2024

For those who faced the same problem with me, this is the code in JS

const crypto = require("crypto");

function validate(data, token) {
  const secretKey = crypto.createHash("sha256").update(token).digest();
  const data_check_string = Object.keys(message)
    .filter((key) => key !== "hash")
    .map((key) => `${key}=${message[key]}`)
    .sort()
    .join("\n");
  const check_hash = crypto
    .createHmac("sha256", secretKey)
    .update(data_check_string)
    .digest("hex");

  return check_hash == data.hash;
}

Provided By Link

Good day, i dont know if you could help me, no matter what i do i kept getting a mismatch, please how do i prepare the data from front-end before sending it to backend for hashing? do i need to hash it at the frontend before sending it to the backend? i will really appreciate your help.

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