Authentification Telegram WebApp sur JavaScript
Aujourd'hui, je vais parler du mécanisme délicat d'authentification dans Telegram WebApp Bot. Qu'est-ce que le bot Telegram WebApp ? C'est juste une possibilité d'exécuter WebView avec votre site Web dans Telegram. Vous pouvez en savoir plus ici .
Pourquoi avons-nous besoin d'authentification ici ?
Votre site Web est censé être ouvert uniquement dans Telegram WebView. Alors, que se passerait-il si quelqu'un faisait cela dans un navigateur ? Les "pirates" peuvent utiliser les données, les identifiants, etc. de faux utilisateurs. Nous devons protéger notre API des personnes extérieures à Telegram.
Comment?
Telegram utilise HMAC (code d'authentification de message basé sur le hachage). Ainsi, si vous initialisez Telegram SDK sur votre site Web, vous pourrez utiliser ces données pour identifier l'utilisateur. Allons créer un mécanisme d'authentification étape par étape.
Étape 1 : transmettre des données via des requêtes
Telegram SDK configure les données de l'utilisateur dans la portée globale de votre application. Il existe des données sur les utilisateurs, leurs smartphones, les thèmes de couleur, etc. Vous pouvez le trouver ici:
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;
Étape 2 : création du middleware Auth
J'utilise Nest.js dans mon projet, mais la façon de créer un middleware est presque la même dans Express.js et Nest.js
Premièrement, nous devons créer le middleware avec quelques lignes de code :
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();
}
}
Étape 3 : analyse de initData
Je vais décrire le processus et ensuite je vais vous montrer le code.
- Nous devons analyser la chaîne initData
- Prenez le champ de hachage de cette chaîne et conservez-le pour le futur
- Trier le reste des champs par ordre alphabétique
- Joignez ces champs à l'aide d'un saut de ligne (\n). Pourquoi? Juste parce que ! Telegram le veut !
Regardons le code :
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'),
},
};
}
C'est le dernier chapitre de notre voyage. Nous devons analyser initData en utilisant la fonction de l'étape précédente et un peu de cryptographie.
Nous devrions suivre ce chemin:
- Écrire une fonction pour encoder un message en utilisant l'algorithme sh256 et une clé
- Analyser la chaîne à l'aide de la fonction de l'étape précédente
- Créez une clé secrète en codant Telegram Bot Token avec la clé "WebAppData"
- Créez un hachage de validation en encodant dataCheckString à partir de la racine précédente avec une clé secrète
- Comparez le hachage de validation avec le hachage de 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;
}
- Vous pouvez ajouter la mise en cache car la cryptographie est une chose assez compliquée pour un processeur, vous pouvez donc utiliser Redis ou même in-memory-cache pour conserver la chaîne initData comme une clé et userData JSON comme valeur par exemple
- Vous pouvez générer votre propre jeton JWT une fois après avoir validé initData et vous pouvez le définir dans des cookies. C'est un moyen plus puissant de créer une authentification, je pense.
![Qu'est-ce qu'une liste liée, de toute façon? [Partie 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































