Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 1m41s
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
|
|
export interface TelegramInitDataUser {
|
|
id: string;
|
|
username?: string;
|
|
firstName?: string;
|
|
lastName?: string;
|
|
}
|
|
|
|
/**
|
|
* Validates Telegram Mini App initData against the bot token.
|
|
* https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app
|
|
*/
|
|
export function verifyTelegramInitData(
|
|
initData: string,
|
|
botToken: string,
|
|
maxAgeSeconds = 86400,
|
|
): TelegramInitDataUser | null {
|
|
if (!initData || initData.length > 4096 || !botToken) return null;
|
|
const params = new URLSearchParams(initData);
|
|
if (new Set(params.keys()).size !== [...params.keys()].length) return null;
|
|
const hash = params.get("hash");
|
|
if (!hash || !/^[a-f0-9]{64}$/.test(hash)) return null;
|
|
params.delete("hash");
|
|
const checkString = [...params.keys()]
|
|
.sort()
|
|
.map((key) => `${key}=${params.get(key)}`)
|
|
.join("\n");
|
|
const secret = createHmac("sha256", "WebAppData").update(botToken).digest();
|
|
const expected = createHmac("sha256", secret).update(checkString).digest("hex");
|
|
const valid =
|
|
hash.length === expected.length &&
|
|
timingSafeEqual(Buffer.from(hash), Buffer.from(expected));
|
|
if (!valid) return null;
|
|
const authDate = Number(params.get("auth_date"));
|
|
if (!Number.isFinite(authDate) || Math.abs(Date.now() / 1000 - authDate) > maxAgeSeconds)
|
|
return null;
|
|
const raw = params.get("user");
|
|
if (!raw) return null;
|
|
try {
|
|
const user = JSON.parse(raw) as {
|
|
id?: unknown;
|
|
username?: unknown;
|
|
first_name?: unknown;
|
|
last_name?: unknown;
|
|
};
|
|
if (typeof user.id !== "number" && typeof user.id !== "string") return null;
|
|
return {
|
|
id: String(user.id),
|
|
...(typeof user.username === "string" ? { username: user.username } : {}),
|
|
...(typeof user.first_name === "string" ? { firstName: user.first_name } : {}),
|
|
...(typeof user.last_name === "string" ? { lastName: user.last_name } : {}),
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|