mirror of
https://github.com/plankanban/planka.git
synced 2025-07-24 15:49:46 +02:00
feat: Invalidate access token on logout
This commit is contained in:
parent
640908320a
commit
8109936ce2
26 changed files with 242 additions and 37 deletions
|
@ -40,24 +40,33 @@ module.exports = {
|
|||
},
|
||||
|
||||
async fn(inputs) {
|
||||
const remoteAddress = getRemoteAddress(this.req);
|
||||
|
||||
const user = await sails.helpers.users.getOneByEmailOrUsername(inputs.emailOrUsername);
|
||||
|
||||
if (!user) {
|
||||
sails.log.warn(
|
||||
`Invalid email or username: "${inputs.emailOrUsername}"! (IP: ${getRemoteAddress(
|
||||
this.req,
|
||||
)})`,
|
||||
`Invalid email or username: "${inputs.emailOrUsername}"! (IP: ${remoteAddress})`,
|
||||
);
|
||||
throw Errors.INVALID_EMAIL_OR_USERNAME;
|
||||
}
|
||||
|
||||
if (!bcrypt.compareSync(inputs.password, user.password)) {
|
||||
sails.log.warn(`Invalid password! (IP: ${getRemoteAddress(this.req)})`);
|
||||
sails.log.warn(`Invalid password! (IP: ${remoteAddress})`);
|
||||
throw Errors.INVALID_PASSWORD;
|
||||
}
|
||||
|
||||
const accessToken = sails.helpers.utils.createToken(user.id);
|
||||
|
||||
await Session.create({
|
||||
accessToken,
|
||||
remoteAddress,
|
||||
userId: user.id,
|
||||
userAgent: this.req.headers['user-agent'],
|
||||
});
|
||||
|
||||
return {
|
||||
item: sails.helpers.utils.createToken(user.id),
|
||||
item: accessToken,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
16
server/api/controllers/access-tokens/delete.js
Normal file
16
server/api/controllers/access-tokens/delete.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
module.exports = {
|
||||
async fn() {
|
||||
const { accessToken } = this.req;
|
||||
|
||||
await Session.updateOne({
|
||||
accessToken,
|
||||
deletedAt: null,
|
||||
}).set({
|
||||
deletedAt: new Date().toUTCString(),
|
||||
});
|
||||
|
||||
return {
|
||||
item: accessToken,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -1,6 +1,8 @@
|
|||
const bcrypt = require('bcrypt');
|
||||
const zxcvbn = require('zxcvbn');
|
||||
|
||||
const { getRemoteAddress } = require('../../../utils/remoteAddress');
|
||||
|
||||
const Errors = {
|
||||
USER_NOT_FOUND: {
|
||||
userNotFound: 'User not found',
|
||||
|
@ -71,6 +73,13 @@ module.exports = {
|
|||
if (user.id === currentUser.id) {
|
||||
const accessToken = sails.helpers.utils.createToken(user.id, user.passwordUpdatedAt);
|
||||
|
||||
await Session.create({
|
||||
accessToken,
|
||||
userId: user.id,
|
||||
remoteAddress: getRemoteAddress(this.req),
|
||||
userAgent: this.req.headers['user-agent'],
|
||||
});
|
||||
|
||||
return {
|
||||
item: user,
|
||||
included: {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
const { v4: uuid } = require('uuid');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
module.exports = {
|
||||
|
@ -24,6 +25,9 @@ module.exports = {
|
|||
exp: iat + sails.config.custom.tokenExpiresIn * 24 * 60 * 60,
|
||||
},
|
||||
sails.config.session.secret,
|
||||
{
|
||||
keyid: uuid(),
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -17,6 +17,15 @@ module.exports = function defineCurrentUserHook(sails) {
|
|||
return null;
|
||||
}
|
||||
|
||||
const session = await Session.findOne({
|
||||
accessToken,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const user = await sails.helpers.users.getOne(payload.subject);
|
||||
|
||||
if (user && user.passwordChangedAt > payload.issuedAt) {
|
||||
|
@ -43,8 +52,14 @@ module.exports = function defineCurrentUserHook(sails) {
|
|||
|
||||
if (authorizationHeader && TOKEN_PATTERN.test(authorizationHeader)) {
|
||||
const accessToken = authorizationHeader.replace(TOKEN_PATTERN, '');
|
||||
const currentUser = await getUser(accessToken);
|
||||
|
||||
req.currentUser = await getUser(accessToken);
|
||||
if (currentUser) {
|
||||
Object.assign(req, {
|
||||
accessToken,
|
||||
currentUser,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return next();
|
||||
|
@ -52,8 +67,17 @@ module.exports = function defineCurrentUserHook(sails) {
|
|||
},
|
||||
'/attachments/*': {
|
||||
async fn(req, res, next) {
|
||||
if (req.cookies.accessToken) {
|
||||
req.currentUser = await getUser(req.cookies.accessToken);
|
||||
const { accessToken } = req.cookies;
|
||||
|
||||
if (accessToken) {
|
||||
const currentUser = await getUser(accessToken);
|
||||
|
||||
if (currentUser) {
|
||||
Object.assign(req, {
|
||||
accessToken,
|
||||
currentUser,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return next();
|
||||
|
|
49
server/api/models/Session.js
Executable file
49
server/api/models/Session.js
Executable file
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Session.js
|
||||
*
|
||||
* @description :: A model definition represents a database table/collection.
|
||||
* @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
attributes: {
|
||||
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
|
||||
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
|
||||
// ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
|
||||
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
columnName: 'access_token',
|
||||
},
|
||||
remoteAddress: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
columnName: 'remote_address',
|
||||
},
|
||||
userAgent: {
|
||||
type: 'string',
|
||||
isNotEmptyString: true,
|
||||
allowNull: true,
|
||||
columnName: 'user_agent',
|
||||
},
|
||||
deletedAt: {
|
||||
type: 'ref',
|
||||
columnName: 'deleted_at',
|
||||
},
|
||||
|
||||
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
|
||||
// ║╣ ║║║╠╩╗║╣ ║║╚═╗
|
||||
// ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
|
||||
|
||||
// ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
|
||||
// ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗
|
||||
// ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
|
||||
|
||||
userId: {
|
||||
model: 'User',
|
||||
required: true,
|
||||
columnName: 'user_id',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
module.exports.routes = {
|
||||
'POST /api/access-tokens': 'access-tokens/create',
|
||||
'DELETE /api/access-tokens/me': 'access-tokens/delete',
|
||||
|
||||
'GET /api/users': 'users/index',
|
||||
'POST /api/users': 'users/create',
|
||||
|
|
24
server/db/migrations/20220906094517_create_session_table.js
Normal file
24
server/db/migrations/20220906094517_create_session_table.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
module.exports.up = (knex) =>
|
||||
knex.schema.createTable('session', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.text('access_token').notNullable();
|
||||
table.text('remote_address').notNullable();
|
||||
table.text('user_agent');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
table.timestamp('deleted_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('user_id');
|
||||
table.unique('access_token');
|
||||
table.index('remote_address');
|
||||
});
|
||||
|
||||
module.exports.down = (knex) => knex.schema.dropTable('session');
|
Loading…
Add table
Add a link
Reference in a new issue