Feature/jwt auth (#75)

* initial changes for jwt auth

* return permissions as an array of string

* Add JWT expiration environment variable (#74)

* allow fromHost in createCall REST API

* add JWT_EXPIRES_IN=<mins> env variable, 60 mins by default

* add jwt expiration in register.js and signin.js

* fix tests - add permissions and scope to encoded obj in jwt

Co-authored-by: Dave Horton <daveh@beachdognet.com>
Co-authored-by: eglehelms <e.helms@cognigy.com>

* return only the jwt-token in the api response

Co-authored-by: EgleH <egle.helms@gmail.com>
Co-authored-by: eglehelms <e.helms@cognigy.com>
This commit is contained in:
Dave Horton
2022-10-27 07:39:11 -04:00
committed by GitHub
co-authored by eglehelms EgleH
parent 110c4ed0d8
commit 35214a04dc
13 changed files with 234 additions and 68 deletions
+47 -41
View File
@@ -1,12 +1,19 @@
const router = require('express').Router();
const {getMysqlConnection} = require('../../db');
const jwt = require('jsonwebtoken');
const {verifyPassword} = require('../../utils/password-utils');
const {promisePool} = require('../../db');
const sysError = require('../error');
const retrievePemissionsSql = `
SELECT p.name
FROM permissions p, user_permissions up
WHERE up.permission_sid = p.permission_sid
AND up.user_sid = ?
`;
const retrieveSql = 'SELECT * from users where name = ?';
const tokenSql = 'SELECT token from api_keys where account_sid IS NULL AND service_provider_sid IS NULL';
router.post('/', (req, res) => {
router.post('/', async(req, res) => {
const logger = req.app.locals.logger;
const {username, password} = req.body;
if (!username || !password) {
@@ -14,48 +21,47 @@ router.post('/', (req, res) => {
return res.sendStatus(400);
}
getMysqlConnection((err, conn) => {
if (err) {
logger.error({err}, 'Error getting db connection');
try {
const [r] = await promisePool.query(retrieveSql, username);
if (r.length === 0) {
logger.info(`Failed login attempt for user ${username}`);
return res.sendStatus(403);
}
logger.info({r}, 'successfully retrieved user account');
const isCorrect = await verifyPassword(r[0].hashed_password, password);
if (!isCorrect) return res.sendStatus(403);
const force_change = !!r[0].force_change;
const [t] = await promisePool.query(tokenSql);
if (t.length === 0) {
logger.error('Database has no admin token provisioned...run reset_admin_password');
return res.sendStatus(500);
}
conn.query(retrieveSql, [username], async(err, results) => {
conn.release();
if (err) {
logger.error({err}, 'Error getting db connection');
return res.sendStatus(500);
if (process.env.JAMBONES_AUTH_USE_JWT) {
const [p] = await promisePool.query(retrievePemissionsSql, r[0].user_sid);
const permissions = p.map((x) => x.name);
const obj = {user_sid: r[0].user_sid, scope: 'admin', force_change, permissions};
if (r[0].service_provider_sid) {
obj.scope = 'service-provider';
obj.service_provider_sid = r[0].service_provider_sid;
}
if (0 === results.length) {
logger.info(`Failed login attempt for user ${username}`);
return res.sendStatus(403);
else if (r[0].account_sid) {
obj.scope = 'account';
obj.account_sid = r[0].account_sid;
}
logger.info({results}, 'successfully retrieved account');
const isCorrect = await verifyPassword(results[0].hashed_password, password);
if (!isCorrect) return res.sendStatus(403);
const force_change = !!results[0].force_change;
getMysqlConnection((err, conn) => {
if (err) {
logger.error({err}, 'Error getting db connection');
return res.sendStatus(500);
}
conn.query(tokenSql, (err, tokenResults) => {
conn.release();
if (err) {
logger.error({err}, 'Error getting db connection');
return res.sendStatus(500);
}
if (0 === tokenResults.length) {
logger.error('Database has no admin token provisioned...run reset_admin_password');
return res.sendStatus(500);
}
res.json({user_sid: results[0].user_sid, force_change, token: tokenResults[0].token});
});
});
});
});
const token = jwt.sign(
obj,
process.env.JWT_SECRET,
{ expiresIn: parseInt(process.env.JWT_EXPIRES_IN || 60) * 60 }
);
res.json({token});
}
else {
res.json({user_sid: r[0].user_sid, force_change, token: t[0].token});
}
} catch (err) {
sysError(logger, res, err);
}
});
+1 -1
View File
@@ -347,7 +347,7 @@ router.post('/', async(req, res) => {
account_sid: userProfile.account_sid,
email: userProfile.email,
name: userProfile.name
}, process.env.JWT_SECRET, { expiresIn: '1h' });
}, process.env.JWT_SECRET, { expiresIn: parseInt(process.env.JWT_EXPIRES_IN || 60) * 60 });
logger.debug({
user_sid: userProfile.user_sid,
+1 -1
View File
@@ -68,7 +68,7 @@ router.post('/', async(req, res) => {
const token = jwt.sign({
user_sid: userProfile.user_sid,
account_sid: userProfile.account_sid
}, process.env.JWT_SECRET, { expiresIn: '1h' });
}, process.env.JWT_SECRET, { expiresIn: parseInt(process.env.JWT_EXPIRES_IN || 60) * 60 });
logger.debug({
user_sid: userProfile.user_sid,