Skip to content

Instantly share code, notes, and snippets.

@btspoony
Created December 27, 2024 14:34
Show Gist options
  • Save btspoony/f27dd2377d378ed579172548eae4f538 to your computer and use it in GitHub Desktop.
Save btspoony/f27dd2377d378ed579172548eae4f538 to your computer and use it in GitHub Desktop.
Telegram initData verifier
import {
charStringToUint8Array,
hexStringToUint8Array,
} from './utilities.ts';
import nacl from 'tweetnacl';
import { atob } from 'js-base64';
export function parseQueryStr(initData: string): Record<string, string> {
const data = initData || import.meta.env.PUBLIC_TELEGRAM_USER_DATA; // For test using PUBLIC_TELEGRAM_USER_DATA env
const result: Record<string, string> = {};
const params = data.split('&');
for (const param of params) {
const [key, value] = param.split('=');
result[key] =
key === 'hash' || key === 'signature' ? value : decodeURIComponent(value);
}
return result;
}
export function verifyFromThirdParty(obj: Record<string, string>): boolean {
if (typeof obj.signature !== 'string') {
return false;
}
const urlParams: string[] = [];
for (const key in obj) {
if (key !== 'hash' && key !== 'signature') {
urlParams.push(`${key}=${obj[key]}`);
}
}
urlParams.sort();
const urlStr = urlParams.join('\n');
const botId = import.meta.env.PUBLIC_TELEGRAM_BOT_ID;
const checkString = `${botId}:WebAppData\n${urlStr}`;
const telegramPublicKey =
'e7bf03a2fa4602af4580703d88dda5bb59f32ed8b02a56c187fe7d34caed242d'; // Telegram's public key for production
// const telegramPublicKey =
// '40055058a4ee38156a06562e52eece92a771bcd8346a8c4615cb7376eddf72ec'; // Telegram's public key for telegram test environment
const sig = base64UrlDecode(obj.signature);
const message = new TextEncoder().encode(checkString);
const signatureUint8Array = charStringToUint8Array(sig);
const publicKeyUint8Array = hexStringToUint8Array(telegramPublicKey);
let isValid = false;
try {
isValid = nacl.sign.detached.verify(
message,
signatureUint8Array,
publicKeyUint8Array
);
} catch (error) {
console.error(error);
}
return isValid;
}
function base64UrlDecode(str: string) {
console.log('Original', str);
str = str.replace(/-/g, '+').replace(/_/g, '/');
while (str.length % 4) {
str += '=';
}
console.log('Base64UrlDecode', str);
return atob(str);
}
export function hexStringToUint8Array(hex: string) {
if (hex.length % 2 !== 0) {
throw new Error('Invalid hex string');
}
const byteArray = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
byteArray[i / 2] = parseInt(hex.substring(i, i + 2), 16);
}
return byteArray;
}
export function charStringToUint8Array(str: string) {
const byteArray = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
byteArray[i] = str.charCodeAt(i);
}
return byteArray;
}
@btspoony
Copy link
Author

btspoony commented Dec 27, 2024

| How to use

set PUBLIC_TELEGRAM_BOT_ID, which is the number before : in your telegram bot token.

verifyFromThirdParty(parseQueryStr(WebApp.initData))

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