1
Fork 0
mirror of https://github.com/Steffo99/festa.git synced 2024-10-16 23:17:26 +00:00
festa/components/auth/telegram/processing.ts

142 lines
4.9 KiB
TypeScript
Raw Permalink Normal View History

2022-06-11 03:08:49 +00:00
import { default as TelegramLoginButton, TelegramLoginResponse } from "react-telegram-login"
2022-06-08 02:55:16 +00:00
import { default as nodecrypto } from "crypto"
2022-06-11 03:08:49 +00:00
import { AccountTelegram } from "@prisma/client"
2022-05-29 02:01:56 +00:00
/**
2022-06-11 03:08:49 +00:00
* A {@link TelegramLoginResponse} object extended with various utility methods, and with its fields renamed to follow the camelCase JS convention.
2022-05-29 02:01:56 +00:00
*/
2022-06-11 03:08:49 +00:00
export class TelegramLoginObject {
2022-05-29 02:01:56 +00:00
id: number
firstName: string
lastName?: string
username?: string
photoUrl?: string
2022-06-11 03:08:49 +00:00
lang?: string
2022-05-29 02:01:56 +00:00
authDate: Date
hash: string
/**
2022-06-11 03:08:49 +00:00
* Construct a {@link TelegramLoginObject} object from a {@link TelegramLoginData}.
2022-05-29 02:01:56 +00:00
*
* @param u The {@link TelegramLoginData} to use.
*/
2022-06-11 03:08:49 +00:00
constructor(u: TelegramLoginResponse) {
if (!u.id) throw new Error("Missing `id`")
if (!u.first_name) throw new Error("Missing `first_name`")
if (!u.auth_date) throw new Error("Missing `auth_date`")
if (!u.hash) throw new Error("Missing `hash`")
2022-05-29 02:01:56 +00:00
this.id = u.id
this.firstName = u.first_name
this.lastName = u.last_name
this.username = u.username
this.photoUrl = u.photo_url
this.authDate = new Date(u.auth_date * 1000)
// https://stackoverflow.com/a/12372720/4334568
2022-06-11 03:08:49 +00:00
if (isNaN(this.authDate.getTime())) throw new Error("Invalid `auth_date`")
2022-05-29 02:01:56 +00:00
this.hash = u.hash
this.lang = u.lang
}
/**
2022-06-11 03:08:49 +00:00
* Convert this object back into a {@link TelegramLoginResponse}.
2022-05-29 02:01:56 +00:00
*
2022-06-11 03:08:49 +00:00
* @return The {@link TelegramLoginResponse} object, ready to be serialized.
2022-05-29 02:01:56 +00:00
*/
2022-06-11 03:08:49 +00:00
toObject(): TelegramLoginResponse {
2022-05-29 02:01:56 +00:00
return {
id: this.id,
first_name: this.firstName,
last_name: this.lastName,
username: this.username,
photo_url: this.photoUrl,
lang: this.lang,
auth_date: this.authDate.getTime() / 1000,
hash: this.hash,
}
}
/**
* Convert this object into a partial {@link AccountTelegram} database object.
*/
2022-06-11 03:08:49 +00:00
toDatabase(): Pick<AccountTelegram, "telegramId" | "firstName" | "lastName" | "username" | "photoUrl" | "lang"> {
2022-05-29 02:01:56 +00:00
return {
telegramId: this.id,
firstName: this.firstName,
lastName: this.lastName ?? null,
username: this.username ?? null,
photoUrl: this.photoUrl ?? null,
lang: this.lang ?? null,
}
}
/**
* Convert this object in a string, using [the format required to verify a Telegram Login](https://core.telegram.org/widgets/login#checking-authorization).
*
* @param data The data to encode.
* @returns The stringified data.
*/
toString(): string {
const string = Object.entries(this.toObject())
.filter(([key, _]) => key !== "hash")
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => `${key}=${value}`)
.sort()
.join("\n")
return string
}
/**
2022-06-11 03:08:49 +00:00
* Check if the `authDate` of the response is recent: it must be in the past, but within `maxMs` from the current date.
2022-05-29 02:01:56 +00:00
*
* @param maxMs The maximum number of milliseconds that may pass after authentication for the response to be considered valid.
* @returns `true` if the request was sent within the requested timeframe, `false` otherwise.
*/
isRecent(maxMs: number): boolean {
const diff = new Date().getTime() - this.authDate.getTime()
return diff <= maxMs
}
/**
* Calculate the "`hash`" of this object using [the Telegram Login verification procedure](https://core.telegram.org/widgets/login#checking-authorization).
*
* _Only works on Node.js, due to usage of the `crypto` module._
*
* @param token The bot token used to validate the signature.
* @returns The calculated value of the `hash` parameter.
*/
2022-06-11 03:08:49 +00:00
hmac(token: string): string {
2022-05-29 02:01:56 +00:00
const hash = nodecrypto.createHash("sha256")
hash.update(token)
const hmac = nodecrypto.createHmac("sha256", hash.digest())
hmac.update(this.toString())
return hmac.digest("hex")
}
/**
* Validate this object using [the Telegram Login verification procedure](https://core.telegram.org/widgets/login#checking-authorization).
*
* _Only works on Node.js, due to usage of the `crypto` module._
*
* @param token The bot token used to validate the signature.
* @returns `true` if the hash matches the value calculated with {@link hmac}, `false` otherwise.
*/
isValid(token: string): boolean {
const received = this.hash
const calculated = this.hmac(token)
return received === calculated
}
/**
* Get the Telegram "displayed name" of the user represented by this object.
*/
toTelegramName(): string {
2022-06-11 03:08:49 +00:00
if (this.username) return this.username
else if (this.lastName) return this.firstName + " " + this.lastName
2022-05-29 02:01:56 +00:00
else return this.firstName
}
}