added refresh token functionality
This commit is contained in:
parent
adb27445ff
commit
42391cdf7f
|
|
@ -1,3 +1,10 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
secret: "bullpen-secret-key"
|
secret: "bullpen-secret-key",
|
||||||
|
|
||||||
|
// jwtExpiration: 3600, // 1 hour
|
||||||
|
// jwtRefreshExpiration: 86400, // 24 hours
|
||||||
|
|
||||||
|
/* for test */
|
||||||
|
jwtExpiration: 60, // 1 minute
|
||||||
|
jwtRefreshExpiration: 120 // 2 minutes
|
||||||
};
|
};
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
const db = require("../models");
|
const db = require("../models");
|
||||||
const config = require("../config/auth.config");
|
const config = require("../config/auth.config");
|
||||||
const User = db.user;
|
const { user: User, role: Role, refreshToken: RefreshToken } = db;
|
||||||
const Role = db.role;
|
|
||||||
|
|
||||||
const Op = db.Sequelize.Op;
|
const Op = db.Sequelize.Op;
|
||||||
|
|
||||||
|
|
@ -47,16 +46,12 @@ exports.signin = (req, res) => {
|
||||||
email: req.body.email
|
email: req.body.email
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(user => {
|
.then(async (user) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return res.status(404).send({ message: "User Not found." });
|
return res.status(404).send({ message: "User Not found." });
|
||||||
}
|
}
|
||||||
|
|
||||||
const passwordIsValid = user.validPassword(req.body.password);
|
const passwordIsValid = user.validPassword(req.body.password);
|
||||||
// const passwordIsValid = bcrypt.compareSync(
|
|
||||||
// req.body.password,
|
|
||||||
// user.password
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (!passwordIsValid) {
|
if (!passwordIsValid) {
|
||||||
return res.status(401).send({
|
return res.status(401).send({
|
||||||
|
|
@ -70,9 +65,11 @@ exports.signin = (req, res) => {
|
||||||
{
|
{
|
||||||
algorithm: 'HS256',
|
algorithm: 'HS256',
|
||||||
allowInsecureKeySizes: true,
|
allowInsecureKeySizes: true,
|
||||||
expiresIn: 86400, // 24 hours
|
expiresIn: config.jwtExpiration
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let refreshToken = await RefreshToken.createToken(user);
|
||||||
|
|
||||||
const authorities = [];
|
const authorities = [];
|
||||||
user.getRoles().then(roles => {
|
user.getRoles().then(roles => {
|
||||||
for (let i = 0; i < roles.length; i++) {
|
for (let i = 0; i < roles.length; i++) {
|
||||||
|
|
@ -83,7 +80,8 @@ exports.signin = (req, res) => {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
roles: authorities,
|
roles: authorities,
|
||||||
accessToken: token
|
accessToken: token,
|
||||||
|
refreshToken: refreshToken
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
@ -91,3 +89,43 @@ exports.signin = (req, res) => {
|
||||||
res.status(500).send({ message: err.message });
|
res.status(500).send({ message: err.message });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.refreshToken = async (req, res) => {
|
||||||
|
const { refreshToken: requestToken } = req.body;
|
||||||
|
|
||||||
|
if (requestToken == null) {
|
||||||
|
return res.status(403).json({ message: "Refresh Token is required!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let refreshToken = await RefreshToken.findOne({ where: { token: requestToken } });
|
||||||
|
|
||||||
|
console.log(refreshToken)
|
||||||
|
|
||||||
|
if (!refreshToken) {
|
||||||
|
res.status(403).json({ message: "Refresh token is not in database!" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RefreshToken.verifyExpiration(refreshToken)) {
|
||||||
|
RefreshToken.destroy({ where: { id: refreshToken.id } });
|
||||||
|
|
||||||
|
res.status(403).json({
|
||||||
|
message: "Refresh token was expired. Please make a new signin request",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await refreshToken.getUser();
|
||||||
|
let newAccessToken = jwt.sign({ id: user.id }, config.secret, {
|
||||||
|
expiresIn: config.jwtExpiration,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
accessToken: newAccessToken,
|
||||||
|
refreshToken: refreshToken.token,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(500).send({ message: err });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,29 +3,33 @@ const config = require("../config/auth.config.js");
|
||||||
const db = require("../models");
|
const db = require("../models");
|
||||||
const User = db.user;
|
const User = db.user;
|
||||||
|
|
||||||
verifyToken = (req, res, next) => {
|
const { TokenExpiredError } = jwt;
|
||||||
|
|
||||||
|
const catchError = (err, res) => {
|
||||||
|
if (err instanceof TokenExpiredError) {
|
||||||
|
return res.status(401).send({ message: "Unauthorized! Access Token was expired!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.sendStatus(401).send({ message: "Unauthorized!" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyToken = (req, res, next) => {
|
||||||
let token = req.headers["x-access-token"];
|
let token = req.headers["x-access-token"];
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return res.status(403).send({
|
return res.status(403).send({ message: "No token provided!" });
|
||||||
message: "No token provided!"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jwt.verify(token,
|
jwt.verify(token, config.secret, (err, decoded) => {
|
||||||
config.secret,
|
|
||||||
(err, decoded) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(401).send({
|
return catchError(err, res);
|
||||||
message: "Unauthorized!",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
req.userId = decoded.id;
|
req.userId = decoded.id;
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
isAdmin = (req, res, next) => {
|
const isAdmin = (req, res, next) => {
|
||||||
User.findByPk(req.userId).then(user => {
|
User.findByPk(req.userId).then(user => {
|
||||||
user.getRoles().then(roles => {
|
user.getRoles().then(roles => {
|
||||||
for (let i = 0; i < roles.length; i++) {
|
for (let i = 0; i < roles.length; i++) {
|
||||||
|
|
@ -42,7 +46,7 @@ isAdmin = (req, res, next) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
isModerator = (req, res, next) => {
|
const isModerator = (req, res, next) => {
|
||||||
User.findByPk(req.userId).then(user => {
|
User.findByPk(req.userId).then(user => {
|
||||||
user.getRoles().then(roles => {
|
user.getRoles().then(roles => {
|
||||||
for (let i = 0; i < roles.length; i++) {
|
for (let i = 0; i < roles.length; i++) {
|
||||||
|
|
@ -59,7 +63,7 @@ isModerator = (req, res, next) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
isModeratorOrAdmin = (req, res, next) => {
|
const isModeratorOrAdmin = (req, res, next) => {
|
||||||
User.findByPk(req.userId).then(user => {
|
User.findByPk(req.userId).then(user => {
|
||||||
user.getRoles().then(roles => {
|
user.getRoles().then(roles => {
|
||||||
for (let i = 0; i < roles.length; i++) {
|
for (let i = 0; i < roles.length; i++) {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ db.role = require("../models/role.model.js")(sequelize);
|
||||||
db.pitchType = require("../models/pitchType.model.js")(sequelize);
|
db.pitchType = require("../models/pitchType.model.js")(sequelize);
|
||||||
db.pitch = require("../models/pitch.model.js")(sequelize);
|
db.pitch = require("../models/pitch.model.js")(sequelize);
|
||||||
db.bullpenSession = require("../models/bullpenSession.model.js")(sequelize);
|
db.bullpenSession = require("../models/bullpenSession.model.js")(sequelize);
|
||||||
|
db.refreshToken = require("../models/refreshToken.model.js")(sequelize);
|
||||||
|
|
||||||
db.role.belongsToMany(db.user, {
|
db.role.belongsToMany(db.user, {
|
||||||
through: "UserRoles"
|
through: "UserRoles"
|
||||||
|
|
@ -41,6 +42,12 @@ db.role.belongsToMany(db.user, {
|
||||||
db.user.belongsToMany(db.role, {
|
db.user.belongsToMany(db.role, {
|
||||||
through: "UserRoles"
|
through: "UserRoles"
|
||||||
});
|
});
|
||||||
|
db.refreshToken.belongsTo(db.user, {
|
||||||
|
foreignKey: 'userId', targetKey: 'id'
|
||||||
|
});
|
||||||
|
db.user.hasOne(db.refreshToken, {
|
||||||
|
foreignKey: 'userId', targetKey: 'id'
|
||||||
|
});
|
||||||
|
|
||||||
db.ROLES = ["user", "admin", "moderator"];
|
db.ROLES = ["user", "admin", "moderator"];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
const config = require("../config/auth.config");
|
||||||
|
const { v4: uuidv4 } = require("uuid");
|
||||||
|
|
||||||
|
module.exports = (sequelize, Sequelize) => {
|
||||||
|
const RefreshToken = sequelize.define("refreshToken", {
|
||||||
|
token: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
},
|
||||||
|
expiryDate: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
RefreshToken.createToken = async function (user) {
|
||||||
|
let expiredAt = new Date();
|
||||||
|
|
||||||
|
expiredAt.setSeconds(expiredAt.getSeconds() + config.jwtRefreshExpiration);
|
||||||
|
|
||||||
|
let _token = uuidv4();
|
||||||
|
|
||||||
|
let refreshToken = await this.create({
|
||||||
|
token: _token,
|
||||||
|
userId: user.id,
|
||||||
|
expiryDate: expiredAt.getTime(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return refreshToken.token;
|
||||||
|
};
|
||||||
|
|
||||||
|
RefreshToken.verifyExpiration = (token) => {
|
||||||
|
return token.expiryDate.getTime() < new Date().getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
return RefreshToken;
|
||||||
|
};
|
||||||
|
|
@ -18,7 +18,8 @@
|
||||||
"pg": "^8.13.3",
|
"pg": "^8.13.3",
|
||||||
"pg-hstore": "^2.3.4",
|
"pg-hstore": "^2.3.4",
|
||||||
"sequelize": "^6.37.5",
|
"sequelize": "^6.37.5",
|
||||||
"sqlite3": "^5.1.7"
|
"sqlite3": "^5.1.7",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"sequelize-cli": "^6.6.2"
|
"sequelize-cli": "^6.6.2"
|
||||||
|
|
@ -3258,6 +3259,15 @@
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sequelize/node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/serve-static": {
|
"node_modules/serve-static": {
|
||||||
"version": "1.16.2",
|
"version": "1.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||||
|
|
@ -3891,12 +3901,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/uuid": {
|
"node_modules/uuid": {
|
||||||
"version": "8.3.2",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/esm/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/validator": {
|
"node_modules/validator": {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@
|
||||||
"pg": "^8.13.3",
|
"pg": "^8.13.3",
|
||||||
"pg-hstore": "^2.3.4",
|
"pg-hstore": "^2.3.4",
|
||||||
"sequelize": "^6.37.5",
|
"sequelize": "^6.37.5",
|
||||||
"sqlite3": "^5.1.7"
|
"sqlite3": "^5.1.7",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"sequelize-cli": "^6.6.2"
|
"sequelize-cli": "^6.6.2"
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,5 @@ module.exports = function(app) {
|
||||||
);
|
);
|
||||||
|
|
||||||
app.post("/api/auth/signin", controller.signin);
|
app.post("/api/auth/signin", controller.signin);
|
||||||
|
app.post("/api/auth/refreshtoken", controller.refreshToken);
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue