From 663034522d8c09771e0e735ff21b54ff8c7f2ffd Mon Sep 17 00:00:00 2001 From: Chaxu Garg Date: Sat, 10 Jul 2021 19:22:57 +0530 Subject: [PATCH 1/5] Implemented the endpoint for requesting new request tokens for a client --- package-lock.json | 41 +++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/config/keys.js | 1 + src/data/resourceToken.js | 9 +++++++++ src/routes/auth.js | 37 +++++++++++++++++++++++++++++++++-- src/utils/utils.js | 15 ++++++++++++++ 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/data/resourceToken.js diff --git a/package-lock.json b/package-lock.json index 61fc3b8..85a7e83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4705,6 +4705,47 @@ "readable-stream": "^2.0.2" } }, + "redi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/redi/-/redi-0.1.0.tgz", + "integrity": "sha1-cS478QOkoCt0b6KTSpFd4/Wvp3E=" + }, + "redis": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", + "integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", + "requires": { + "denque": "^1.5.0", + "redis-commands": "^1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + }, + "dependencies": { + "denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + } + } + }, + "redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", diff --git a/package.json b/package.json index 5695921..d5e252e 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "passport-github2": "^0.1.12", "passport-google-oauth": "^2.0.0", "qs": "^6.9.4", + "redi": "^0.1.0", + "redis": "^3.1.2", "safe-regex": "^2.1.1" }, "devDependencies": { diff --git a/src/config/keys.js b/src/config/keys.js index 9f90863..47031bf 100644 --- a/src/config/keys.js +++ b/src/config/keys.js @@ -3,6 +3,7 @@ const path = require('path'); export const expTime = 60 * 20; export const rememberTime = 60 * 60 * 24 * 2; +export const reqExpTime = 50; export const accessTokenName = 'token'; export const refreshTokenName = 'rememberme'; export const iss = 'auth.devclub.in'; diff --git a/src/data/resourceToken.js b/src/data/resourceToken.js new file mode 100644 index 0000000..7a5db0b --- /dev/null +++ b/src/data/resourceToken.js @@ -0,0 +1,9 @@ +import redis from 'redis'; + +const rtokens = redis.createClient(); + +rtokens.on('error', (err) => { + console.log(`Error: ${err}`); +}); + +export default rtokens; diff --git a/src/routes/auth.js b/src/routes/auth.js index 3756241..bc33f72 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,6 +1,8 @@ import express from 'express'; import { verify, decode } from 'jsonwebtoken'; import bcrypt from 'bcryptjs'; +import rtoken from '../data/resourceToken'; +import * as keys from '../config/keys'; import { verifyToken, createJWTCookie, @@ -8,6 +10,8 @@ import { sendVerificationEmail, sendPassResetEmail, linkSocial, + makeid, + sendRequestToken, } from '../utils/utils'; import { accessTokenName, @@ -168,8 +172,6 @@ router.post('/password/reset', async (req, res) => { } }); -export default router; - router.get('/google', (req, res, next) => { passport.authenticate('google', { scope: ['profile', 'email'], @@ -388,3 +390,34 @@ router.post(`/sudoTestCommand/:secret/makeadminforclient`, async (req, res) => { }); } }); + +router.post('/requestToken', async (req, res) => { + try { + const { q } = req.body; + const { clientId } = decode(q); + const client = await Client.findById(clientId); + + if (!client) { + return res.status(400).json({ + err: true, + msg: 'No client found', + }); + } + + verify(q, client.access_token, { + algorithms: ['HS256'], + }); + const requestToken = makeid(64, true); + rtoken.hmset(requestToken.toString(), { cId: clientId }); + rtoken.expire(requestToken.toString(), keys.reqExpTime); + sendRequestToken(requestToken, res); + } catch (error) { + console.log(error); + return res.status(401).json({ + err: true, + msg: 'Unauthorized Client', + }); + } +}); + +export default router; diff --git a/src/utils/utils.js b/src/utils/utils.js index 3e11eef..e687ba0 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -339,6 +339,20 @@ const linkSocial = async (token, provider, uid, email, done) => { return done(null, primary_account); }; +const sendRequestToken = (reqToken, res) => { + const payload = { + request_token: reqToken, + }; + + const token = jwt.sign(payload, keys.privateKey, { + expiresIn: keys.reqExpTime, + algorithm: 'RS256', + }); + + res.send(token); + return token; +}; + export { makeid, createJWTCookie, @@ -351,4 +365,5 @@ export { sendVerificationEmail, sendPassResetEmail, addRoles, + sendRequestToken, }; From 16b17ffb9af05be9cf55f4e497a2916f81167370 Mon Sep 17 00:00:00 2001 From: Chaxu Garg Date: Fri, 16 Jul 2021 22:25:33 +0530 Subject: [PATCH 2/5] Fixed minor bugs/errors --- src/data/resourceToken.js | 2 +- src/routes/auth.js | 28 ++++++++++++++++++++++------ src/utils/utils.js | 9 ++++----- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/data/resourceToken.js b/src/data/resourceToken.js index 7a5db0b..466bc00 100644 --- a/src/data/resourceToken.js +++ b/src/data/resourceToken.js @@ -3,7 +3,7 @@ import redis from 'redis'; const rtokens = redis.createClient(); rtokens.on('error', (err) => { - console.log(`Error: ${err}`); + console.log(err); }); export default rtokens; diff --git a/src/routes/auth.js b/src/routes/auth.js index bc33f72..0279458 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -11,7 +11,7 @@ import { sendPassResetEmail, linkSocial, makeid, - sendRequestToken, + getRequestToken, } from '../utils/utils'; import { accessTokenName, @@ -393,8 +393,8 @@ router.post(`/sudoTestCommand/:secret/makeadminforclient`, async (req, res) => { router.post('/requestToken', async (req, res) => { try { - const { q } = req.body; - const { clientId } = decode(q); + const { jwt } = req.body; + const { clientId } = decode(jwt); const client = await Client.findById(clientId); if (!client) { @@ -404,13 +404,29 @@ router.post('/requestToken', async (req, res) => { }); } - verify(q, client.access_token, { + verify(jwt, client.access_token, { algorithms: ['HS256'], }); - const requestToken = makeid(64, true); + let requestToken = makeid(64, true); + let alreadyExists = false; + do { + let tokenAlreadyExists = false; + rtoken.exists(requestToken.toString(), (_err, exists) => { + if (exists === 1) { + tokenAlreadyExists = true; + } + }); + if (tokenAlreadyExists) { + requestToken = makeid(64, true); + alreadyExists = true; + } else { + alreadyExists = false; + } + } while (alreadyExists); rtoken.hmset(requestToken.toString(), { cId: clientId }); rtoken.expire(requestToken.toString(), keys.reqExpTime); - sendRequestToken(requestToken, res); + const token = getRequestToken(requestToken); + res.send(token); } catch (error) { console.log(error); return res.status(401).json({ diff --git a/src/utils/utils.js b/src/utils/utils.js index e687ba0..f32f81b 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -339,17 +339,16 @@ const linkSocial = async (token, provider, uid, email, done) => { return done(null, primary_account); }; -const sendRequestToken = (reqToken, res) => { +const getRequestToken = (reqToken) => { const payload = { - request_token: reqToken, + requestToken: reqToken, }; const token = jwt.sign(payload, keys.privateKey, { expiresIn: keys.reqExpTime, algorithm: 'RS256', + issuer: keys.iss, }); - - res.send(token); return token; }; @@ -365,5 +364,5 @@ export { sendVerificationEmail, sendPassResetEmail, addRoles, - sendRequestToken, + getRequestToken, }; From 7d98a109b906434449f77f784c5b8fa150d73137 Mon Sep 17 00:00:00 2001 From: Chaxu Garg Date: Thu, 22 Jul 2021 14:54:13 +0530 Subject: [PATCH 3/5] Added check for duplicate request token --- src/routes/auth.js | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/routes/auth.js b/src/routes/auth.js index 0279458..f0154b0 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,6 +1,7 @@ import express from 'express'; import { verify, decode } from 'jsonwebtoken'; import bcrypt from 'bcryptjs'; +import util from 'util'; import rtoken from '../data/resourceToken'; import * as keys from '../config/keys'; import { @@ -407,22 +408,14 @@ router.post('/requestToken', async (req, res) => { verify(jwt, client.access_token, { algorithms: ['HS256'], }); - let requestToken = makeid(64, true); - let alreadyExists = false; - do { - let tokenAlreadyExists = false; - rtoken.exists(requestToken.toString(), (_err, exists) => { - if (exists === 1) { - tokenAlreadyExists = true; - } - }); - if (tokenAlreadyExists) { - requestToken = makeid(64, true); - alreadyExists = true; - } else { - alreadyExists = false; - } - } while (alreadyExists); + rtoken.exists = util.promisify(rtoken.exists); + let requestToken; + let exists = 1; + while (exists) { + requestToken = makeid(64, true); + // eslint-disable-next-line no-await-in-loop + exists = await rtoken.exists(requestToken.toString()); + } rtoken.hmset(requestToken.toString(), { cId: clientId }); rtoken.expire(requestToken.toString(), keys.reqExpTime); const token = getRequestToken(requestToken); @@ -436,4 +429,19 @@ router.post('/requestToken', async (req, res) => { } }); +// api route to check whether the above route works or not + +// router.post('/checkFunction', async (req, res) => { +// let { requestToken } = req.body; +// let exists = 1; +// rtoken.exists = util.promisify(rtoken.exists); +// while (exists) { +// // eslint-disable-next-line no-await-in-loop +// exists = await rtoken.exists(requestToken.toString()); +// requestToken = makeid(64, true); +// console.log(exists); +// } +// res.send(requestToken); +// }); + export default router; From 049c1739ad174f1377a6425c6b05172f922e18f6 Mon Sep 17 00:00:00 2001 From: Chaxu Garg Date: Mon, 23 Aug 2021 15:49:34 +0530 Subject: [PATCH 4/5] Added route for verifying request token and test routes --- src/config/keys.js | 2 +- src/routes/auth.js | 48 ++++++++++++++++++++++++++++++++-------------- src/server.js | 4 ++++ src/tests/tests.js | 35 +++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 src/tests/tests.js diff --git a/src/config/keys.js b/src/config/keys.js index 47031bf..cf70eb4 100644 --- a/src/config/keys.js +++ b/src/config/keys.js @@ -3,7 +3,7 @@ const path = require('path'); export const expTime = 60 * 20; export const rememberTime = 60 * 60 * 24 * 2; -export const reqExpTime = 50; +export const reqExpTime = 60; export const accessTokenName = 'token'; export const refreshTokenName = 'rememberme'; export const iss = 'auth.devclub.in'; diff --git a/src/routes/auth.js b/src/routes/auth.js index f0154b0..9f70a4f 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -429,19 +429,39 @@ router.post('/requestToken', async (req, res) => { } }); -// api route to check whether the above route works or not - -// router.post('/checkFunction', async (req, res) => { -// let { requestToken } = req.body; -// let exists = 1; -// rtoken.exists = util.promisify(rtoken.exists); -// while (exists) { -// // eslint-disable-next-line no-await-in-loop -// exists = await rtoken.exists(requestToken.toString()); -// requestToken = makeid(64, true); -// console.log(exists); -// } -// res.send(requestToken); -// }); +router.get('/verifyRToken', async (req, res) => { + try { + console.log('here'); + const { q } = req.query; + const { requestToken } = decode(q); + rtoken.exists = util.promisify(rtoken.exists); + rtoken.hget = util.promisify(rtoken.hget); + const exists = await rtoken.exists(requestToken.toString()); + console.log(exists); + if (!exists) { + return res.status(401).json({ + err: true, + msg: 'Session Expired', + }); + } + const user = await verifyToken(req, res); + const clientId = await rtoken.hget(requestToken.toString(), 'cId'); + rtoken.hmset(requestToken.toString(), { + cId: clientId, + uId: user._id.toString(), + }); + rtoken.expire(requestToken.toString(), keys.reqExpTime); + return res.status(200).json({ + err: false, + msg: 'User authenticated successfully', + }); + } catch (error) { + console.log(error); + return res.status(401).json({ + err: true, + msg: 'Unauthorized Client', + }); + } +}); export default router; diff --git a/src/server.js b/src/server.js index 2216093..5cc319f 100644 --- a/src/server.js +++ b/src/server.js @@ -8,6 +8,7 @@ import auth from './routes/auth'; import profile from './routes/profile'; import client from './routes/client'; import api from './routes/api'; +import tests from './tests/tests'; import * as keys from './config/keys'; import { socialAuthenticate, linkSocial } from './utils/utils'; @@ -191,6 +192,9 @@ app.use('/profile', profile); app.use('/client', client); app.use('/api', api); +if (process.env.NODE_ENV === 'DEV') { + app.use('/test', tests); +} app.get('/privacy-policy', (req, res) => { res.render('privacy_policy'); }); diff --git a/src/tests/tests.js b/src/tests/tests.js new file mode 100644 index 0000000..85aad95 --- /dev/null +++ b/src/tests/tests.js @@ -0,0 +1,35 @@ +import express from 'express'; +import util from 'util'; +import rtoken from '../data/resourceToken'; +import { makeid } from '../utils/utils'; + +const router = express.Router(); + +// api route to check whether current requestToken exists or not + +router.post('/checkFunction', async (req, res) => { + let { requestToken } = req.body; + let exists = 1; + rtoken.exists = util.promisify(rtoken.exists); + while (exists) { + // eslint-disable-next-line no-await-in-loop + exists = await rtoken.exists(requestToken.toString()); + requestToken = makeid(64, true); + console.log(exists); + } + res.send(requestToken); +}); + +// route to check all the data stored in redis + +router.get('/rediData', (req, res) => { + rtoken.keys('*', (err, keys) => { + // eslint-disable-next-line array-callback-return + keys.map((key) => { + console.log(key); + console.log(rtoken.hmget(key, ['cId', 'uId'])); + }); + }); + res.send(200); +}); +export default router; From cbcea9b0a20e15f62c14873dddd9d8ff04b5e4f3 Mon Sep 17 00:00:00 2001 From: Chaxu Garg Date: Wed, 25 Aug 2021 09:25:17 +0530 Subject: [PATCH 5/5] Added new check in verifyRToken route --- src/routes/auth.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/routes/auth.js b/src/routes/auth.js index 9f70a4f..c79de84 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -446,6 +446,18 @@ router.get('/verifyRToken', async (req, res) => { } const user = await verifyToken(req, res); const clientId = await rtoken.hget(requestToken.toString(), 'cId'); + const client = await Client.findById(clientId); + + if (!client) { + return res.status(400).json({ + err: true, + msg: 'No client found', + }); + } + + verify(requestToken, client.access_token, { + algorithms: ['HS256'], + }); rtoken.hmset(requestToken.toString(), { cId: clientId, uId: user._id.toString(),