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..cf70eb4 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 = 60; 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..466bc00 --- /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(err); +}); + +export default rtokens; diff --git a/src/routes/auth.js b/src/routes/auth.js index 3756241..c79de84 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,6 +1,9 @@ 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 { verifyToken, createJWTCookie, @@ -8,6 +11,8 @@ import { sendVerificationEmail, sendPassResetEmail, linkSocial, + makeid, + getRequestToken, } from '../utils/utils'; import { accessTokenName, @@ -168,8 +173,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 +391,89 @@ router.post(`/sudoTestCommand/:secret/makeadminforclient`, async (req, res) => { }); } }); + +router.post('/requestToken', async (req, res) => { + try { + const { jwt } = req.body; + const { clientId } = decode(jwt); + const client = await Client.findById(clientId); + + if (!client) { + return res.status(400).json({ + err: true, + msg: 'No client found', + }); + } + + verify(jwt, client.access_token, { + algorithms: ['HS256'], + }); + 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); + res.send(token); + } catch (error) { + console.log(error); + return res.status(401).json({ + err: true, + msg: 'Unauthorized Client', + }); + } +}); + +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'); + 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(), + }); + 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; diff --git a/src/utils/utils.js b/src/utils/utils.js index 3e11eef..f32f81b 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -339,6 +339,19 @@ const linkSocial = async (token, provider, uid, email, done) => { return done(null, primary_account); }; +const getRequestToken = (reqToken) => { + const payload = { + requestToken: reqToken, + }; + + const token = jwt.sign(payload, keys.privateKey, { + expiresIn: keys.reqExpTime, + algorithm: 'RS256', + issuer: keys.iss, + }); + return token; +}; + export { makeid, createJWTCookie, @@ -351,4 +364,5 @@ export { sendVerificationEmail, sendPassResetEmail, addRoles, + getRequestToken, };