Autenticazione Telegram WebApp su JavaScript
Oggi parlerò del complicato meccanismo di autenticazione in Telegram WebApp Bot. Cos'è il bot di Telegram WebApp? È solo la possibilità di eseguire WebView con il tuo sito Web all'interno di Telegram. Puoi leggere di più qui .
Perché abbiamo bisogno dell'autenticazione qui?
Il tuo sito web dovrebbe essere aperto solo in Telegram WebView. Quindi, cosa succederebbe se qualcuno lo facesse in un browser? Gli "hacker" possono utilizzare dati utente falsi, ID e così via. Dobbiamo proteggere la nostra API da persone al di fuori di Telegram.
Come?
Telegram utilizza HMAC (codice di autenticazione dei messaggi basato su hash). Quindi, se inizializzi l' SDK di Telegram sul tuo sito web, sarai in grado di utilizzare quei dati per identificare l'utente. Andiamo a creare un meccanismo di autenticazione passo dopo passo.
Passaggio 1: passaggio dei dati tramite le richieste
L'SDK di Telegram imposta i dati dell'utente nell'ambito globale della tua app. Ci sono dati sugli utenti, i loro smartphone, temi di colore e altro ancora. Potete trovare qui:
window.Telegram.WebApp
const { initData } = window.Telegram.WebApp
auth_date=<auth_date>&query_id=<query_id>&user=<user>&hash=<hash>
axios.defaults.headers.common['Telegram-Data'] = window?.Telegram?.WebApp?.initData;
Passaggio 2: creazione del middleware di autenticazione
Uso Nest.js nel mio progetto, ma il modo per creare il middleware è quasi lo stesso in Express.js e Nest.js
Innanzitutto, dovremmo creare il middleware con poche righe di codice:
export function telegramAuthMiddleware(req, res, next) {
// take initData from headers
const iniData = req.headers[
'telegram-data'
];
// use our helpers (see bellow) to validate string
// and get user from it
const user = checkAuthorization(iniData);
// add uses to the request "context" for the future
if (user) {
req.user = user;
next();
// or if the validation is failed response 401
} else {
res.writeHead(401, { 'content-type': 'application/json' });
res.write('unauthorized');
res.end();
}
}
Passaggio 3: analisi del file initData
Descriverò il processo e poi ti mostrerò il codice.
- Dobbiamo analizzare la stringa initData
- Prendi il campo hash da quella stringa e conservalo per il futuro
- Ordina il resto dei campi in ordine alfabetico
- Unisciti a questi campi utilizzando l'interruttore di riga (\n). Come mai? Solo perché! Telegram lo vuole!
Diamo un'occhiata al codice:
function parseAuthString(iniData) {
// parse string to get params
const searchParams = new URLSearchParams(iniData);
// take the hash and remove it from params list
const hash = searchParams.get('hash');
searchParams.delete('hash');
// sort params
const restKeys = Array.from(searchParams.entries());
restKeys.sort(([aKey, aValue], [bKey, bValue]) => aKey.localeCompare(bKey));
// and join it with \n
const dataCheckString = restKeys.map(([n, v]) => `${n}=${v}`).join('\n');
return {
dataCheckString,
hash,
// get metaData from params
metaData: {
user: JSON.parse(searchParams.get('user')),
auth_date: searchParams.get('auth_date'),
query_id: searchParams.get('query_id'),
},
};
}
Questo è l'ultimo capitolo del nostro viaggio. Dobbiamo analizzare initData utilizzando la funzione del passaggio precedente e un po' di crittografia.
Dovremmo seguire questo percorso:
- Scrivi una funzione per codificare il messaggio usando l'algoritmo sh256 e una chiave
- Analizza la stringa utilizzando la funzione del passaggio precedente
- Crea una chiave segreta codificando Telegram Bot Token con la chiave "WebAppData".
- Crea un hash di convalida codificando dataCheckString dalla radice precedente con una chiave segreta
- Confronta l'hash di convalida con l'hash di initData
const crypto = require('crypto')
const WEB_APP_DATA_CONST = "WebAppData"
const TELEGRAM_BOT_TOKEN = "so secret token!!"
// encoding message with key
// we need two types of representation here: Buffer and Hex
function encodeHmac(message, key, repr=undefined) {
return crypto.createHmac('sha256', key).update(message).digest(repr);
}
function checkAuthorization(iniData){
// parsing the iniData sting
const authTelegramData = parseAuthString(iniData);
// creating the secret key and keep it as a Buffer (important!)
const secretKey = encodeHmac(
TELEGRAM_BOT_TOKEN,
WEB_APP_DATA_CONST,
);
// creating the validation key (and transform it to HEX)
const validationKey = encodeHmac(
authTelegramData.dataCheckString,
secretKey,
'hex',
);
// the final step - comparing and returning
if (validationKey === authTelegramData.hash) {
return authTelegramData.metaData.user;
}
return null;
}
const crypto = require('crypto')
const WEB_APP_DATA_CONST = "WebAppData"
const TELEGRAM_BOT_TOKEN = "so secret token!!"
export function telegramAuthMiddleware(req, res, next) {
// take initData from headers
const iniData = req.headers[
'telegram-data'
];
// use our helpers (see bellow) to validate string
// and get user from it
const user = checkAuthorization(iniData);
// add uses to the request "context" for the future
if (user) {
req.user = user;
next();
// or if the validation is failed response 401
} else {
res.writeHead(401, { 'content-type': 'application/json' });
res.write('unauthorized');
res.end();
}
}
function parseAuthString(iniData) {
// parse string to get params
const searchParams = new URLSearchParams(iniData);
// take the hash and remove it from params list
const hash = searchParams.get('hash');
searchParams.delete('hash');
// sort params
const restKeys = Array.from(searchParams.entries());
restKeys.sort(([aKey, aValue], [bKey, bValue]) => aKey.localeCompare(bKey));
// and join it with \n
const dataCheckString = restKeys.map(([n, v]) => `${n}=${v}`).join('\n');
return {
dataCheckString,
hash,
// get metaData from params
metaData: {
user: JSON.parse(searchParams.get('user')),
auth_date: searchParams.get('auth_date'),
query_id: searchParams.get('query_id'),
},
};
}
// encoding message with key
// we need two types of representation here: Buffer and Hex
function encodeHmac(message, key, repr=undefined) {
return crypto.createHmac('sha256', key).update(message).digest(repr);
}
function checkAuthorization(iniData){
// parsing the iniData sting
const authTelegramData = parseAuthString(iniData);
// creating the secret key and keep it as a Buffer (important!)
const secretKey = encodeHmac(
TELEGRAM_BOT_TOKEN,
WEB_APP_DATA_CONST,
);
// creating the validation key (and transform it to HEX)
const validationKey = encodeHmac(
authTelegramData.dataCheckString,
secretKey,
'hex',
);
// the final step - comparing and returning
if (validationKey === authTelegramData.hash) {
return authTelegramData.metaData.user;
}
return null;
}
- Puoi aggiungere la memorizzazione nella cache perché la crittografia è una cosa piuttosto complicata per un processore, quindi puoi usare Redis o anche in-memory-cache per mantenere la stringa initData come una chiave e userData JSON come valore per esempio
- Puoi generare il tuo token JWT una volta dopo aver convalidato initData e puoi impostarlo nei cookie. Penso che sia un modo più potente per creare l'autenticazione.

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































