diff --git a/app.js b/app.js index 0ba3c71..228b314 100644 --- a/app.js +++ b/app.js @@ -5,7 +5,9 @@ const bcrypt = require('bcrypt'); const QRCode = require('qrcode'); const cors = require('cors'); const { createSecureServer } = require('http2'); +const { default: axios } = require('axios'); require("dotenv-flow").config(); +const crypto = require('crypto'); var SequelizeStore = require("connect-session-sequelize")(session.Store); @@ -58,13 +60,10 @@ Contact.sync(); const User = sequelize.define('User', { telegram: { - type: DataTypes.STRING, + type: DataTypes.INTEGER, allowNull: false, unique: true, }, - hash: { - type: STRING, - }, verification: { type: DataTypes.STRING, }, @@ -82,17 +81,36 @@ User.sync().then(() => { }); -function authUser(telegram, password, done) { +function authUser(telegramResponse, done) { + let dataCheckArray = []; + + for (const [key, value] of Object.entries(telegramResponse)) { + if (key == "hash") continue; + dataCheckArray.push(`${key}=${value}`); + } + dataCheckArray.sort(); + const dataCheckString = dataCheckArray.join("\n"); + + secretKey = crypto.createHash("sha256").update(process.env.TELEGRAM_TOKEN).digest(); + confirmationHash = crypto.createHmac("sha256", secretKey).update(dataCheckString).digest("hex"); + + const authorized = confirmationHash == telegramResponse.hash; + + if (!authorized) { + done({ authorized: false }); + } + User.findOne({ where: { - telegram: telegram + telegram: telegramResponse.id, } }).then(user => { if (!user) { - done(false, "User not found") + createUser(telegramResponse.id, (success) => { + done({ authorized: success }); + }) } else { - const auth = bcrypt.compareSync(password, user.hash); - done(auth, auth ? "Authorized" : "Wrong password"); + done({ authorized: true }); } }); } @@ -140,11 +158,9 @@ function checkVerification(id, done) { }); } -function createUser(telegram, password, done) { - hash = bcrypt.hashSync(password, 10); +function createUser(telegram, done) { User.create({ - telegram: telegram, - hash: hash, + telegram: telegram }).then(user => { if (!user) { done(false, "Could not create user"); @@ -162,13 +178,21 @@ function createUser(telegram, password, done) { function addContact(telegram, withUserID, done) { User.findOne({ where: { telegram: telegram } }).then(user => { - Contact.create({ user: user.id, with: withUserID }) - .then(res => { - done(true, "Successfully added contact"); - }) - .catch(e => { - done(false, e); - }); + User.findOne({ where: { id: withUserID } }).then(withUser => { + Contact.create({ user: user.id, with: withUserID }) + .then(res => { + console.log(`Registering contact between ${user.id} and ${withUserID}`); + sendTelegramMessage(withUser.telegram, + "Someone scanned your QR code. You will be notified if they are tested positive with Covid. If you are tested positive, please tell this bot /COVIDPOSITIVE", + () => {} + ); + done(true, "Successfully added contact"); + }) + .catch(e => { + done(false, e); + }); + + }) }) } @@ -176,6 +200,32 @@ function getCookieExpiry() { return new Date(Date.now() + COOKIE_EXPIRY_DURATION); } +function setTelegramWebHook(done) { + const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/setWebhook`; + axios.post(url, { + url: `${process.env.SERVER_API_URL}/${process.env.TELEGRAM_SECRET}`, + allowed_updates: [], + drop_pending_updates: true, + }) + .then(res => { done(res) }) + .catch(err => { done(err) }); +} + +function sendTelegramMessage(telegramID, message, done) { + const url = `https://api.telegram.org/bot${process.env.TELEGRAM_TOKEN}/sendMessage`; + axios.post(url, { + chat_id: telegramID, + text: message, + }) + .then((res) => { + done(res) + }) + .catch(err => { + console.error("Problem sending Telegram message."); + done(err) + }); +} + const app = express(); app.set('trust proxy', 1) app.use(session({ @@ -191,17 +241,22 @@ app.use(session({ app.use(cors({ credentials: true, origin: true, secure: true })); app.use(express.json()) +setTelegramWebHook(() => {}); +app.post(`/${process.env.TELEGRAM_SECRET}`, (req, res) => { + res.send(); +}); + app.post('/login', (req, res) => { - reqTelegram = req.body.telegram.toLowerCase(); - const auth = authUser(reqTelegram, req.body.password, (success, msg) => { + telegramResponse = req.body.telegramResponse; + const auth = authUser(telegramResponse, (success, msg) => { if (success) { const verified = req.session.verified; const verifiedBy = req.session.verifiedBy; req.session.regenerate(() => { cookieExpiry = getCookieExpiry(); - req.session.user = reqTelegram; + req.session.user = telegramResponse.id; if (verified) { - addContact(reqTelegram, verifiedBy, (contactSuccess) => { + addContact(telegramResponse.id, verifiedBy, (contactSuccess) => { res.send({ authorized: success, message: msg, contactSuccess: contactSuccess }); }); } else { @@ -214,25 +269,6 @@ app.post('/login', (req, res) => { }); }); -app.post('/create', (req, res) => { - reqTelegram = req.body.telegram.toLowerCase(); - if (req.session.verified) { - createUser(reqTelegram, req.body.password, (success, msg) => { - cookieExpiry = getCookieExpiry(); - req.session.user = reqTelegram; - if (success) { - addContact(req.session.user, req.session.verifiedBy, (sucesss, msg) => { - res.send({ success: success, message: msg }); - }); - } else { - res.send({ success: success, message: msg }); - } - }); - } else { - res.status(401).send("Not verified"); - } -}) - app.get('/code', (req, res) => { if (!req.session.user) { res.status(401).send("Not logged in"); diff --git a/package-lock.json b/package-lock.json index c32cbe2..5444b11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "axios": "^0.21.1", "bcrypt": "^5.0.1", "connect-session-sequelize": "^7.1.1", "cors": "^2.8.5", @@ -202,6 +203,14 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "optional": true }, + "node_modules/axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dependencies": { + "follow-redirects": "^1.10.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -965,6 +974,25 @@ "node": ">=6" } }, + "node_modules/follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", @@ -3431,6 +3459,14 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "optional": true }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4020,6 +4056,11 @@ "locate-path": "^3.0.0" } }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + }, "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", diff --git a/package.json b/package.json index ab47dcc..5daa337 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^0.21.1", "bcrypt": "^5.0.1", "connect-session-sequelize": "^7.1.1", "cors": "^2.8.5",