zents
Version:
ZenTS is a Node.js & TypeScript MVC-Framework for building rich web applications, released as free and open-source software under the MIT License. It is designed for building web applications with modern tools and design patterns.
168 lines • 6.22 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecurityProvider = void 0;
const enums_1 = require("../types/enums");
const JWT_1 = require("./JWT");
const secure_password_1 = __importDefault(require("secure-password"));
const bcrypt_1 = __importDefault(require("bcrypt"));
const crypto_1 = __importDefault(require("crypto"));
class SecurityProvider {
constructor(options, response, adapter, strategy, databaseContainer) {
this.options = options;
this.response = response;
this.adapter = adapter;
this.strategy = strategy;
this.connection = databaseContainer.get(enums_1.DB_TYPE.ORM);
this.argon2idGenerator =
this.options.algorithm === 'argon2id'
? new secure_password_1.default({
memlimit: options.memLimit,
opslimit: options.opsLimit,
})
: null;
}
async login(context) {
const username = context.body[this.options.usernameField];
const password = context.body[this.options.passwordField];
if (typeof username !== 'string' ||
!username.length ||
typeof password !== 'string' ||
!password.length) {
return this.response.loginFailed(context);
}
const repository = this.connection.getRepository(this.options.userEntity);
const user = await repository.findOne({
username,
});
if (!user ||
typeof user[this.options.passwordColumn] !== 'string' ||
typeof user[this.options.identifierColumn] === 'undefined') {
return this.response.loginFailed(context);
}
const passwordHash = user[this.options.passwordColumn];
if (!(await bcrypt_1.default.compare(password, passwordHash))) {
return this.response.loginFailed(context);
}
const token = await this.generateToken(context, user[this.options.identifierColumn]);
await this.adapter.create(token.sessionId);
this.strategy.setToken(context, token.token);
return this.response.loginSuccess(context, token.token);
}
async logout(context) {
if (!this.strategy.hasToken(context)) {
return this.response.logoutFailed(context);
}
const token = this.strategy.getToken(context);
if (token === false) {
return this.response.forbidden(context);
}
const parsedToken = await this.parseToken(token);
if (parsedToken === false) {
return this.response.forbidden(context);
}
await this.adapter.remove(parsedToken.sessionId);
return this.response.logoutSuccess(context);
}
async authorize(context) {
const isNotAuth = {
isAuth: false,
};
if (!this.strategy.hasToken(context)) {
return isNotAuth;
}
const token = this.strategy.getToken(context);
if (token === false) {
return isNotAuth;
}
const parsedToken = await this.parseToken(token);
if (parsedToken === false || !(await this.adapter.has(parsedToken.sessionId))) {
return isNotAuth;
}
const repository = this.connection.getRepository(this.options.userEntity);
const user = await repository.findOne({
[this.options.identifierColumn]: parsedToken.userId,
});
if (!user) {
return isNotAuth;
}
return {
isAuth: true,
sessionId: parsedToken.sessionId,
user,
};
}
async forbidden(context) {
return new Promise((resolve) => {
this.response.forbidden(context);
resolve();
});
}
async generatePasswordHash(plainTextPassword) {
let hash;
if (this.options.algorithm === 'argon2id') {
const passwordBuffer = Buffer.from(plainTextPassword);
hash = (await this.argon2idGenerator.hash(passwordBuffer)).toString();
}
else if (this.options.algorithm === 'bcrypt') {
hash = await bcrypt_1.default.hash(plainTextPassword, this.options.saltRounds);
}
return hash;
}
generateSessionId() {
return crypto_1.default.randomBytes(16).toString('hex');
}
async generateToken(context, userId) {
const sessionId = this.generateSessionId();
let providers = [
{
provider: this.options.name,
sessionId,
userId,
},
];
if (this.strategy.hasToken(context)) {
const currentToken = this.strategy.getToken(context);
if (await this.verifyToken(currentToken)) {
const currentData = JWT_1.JWT.decode(currentToken);
if (currentData && Array.isArray(currentData.providers)) {
providers = [...providers, ...currentData.providers];
}
}
}
return {
token: await JWT_1.JWT.sign({
providers,
}),
sessionId,
};
}
async verifyToken(token) {
if (typeof token !== 'string' || !token.length) {
return false;
}
const splitted = token.trim().split(' ');
if (splitted.length < 2 || splitted[0].toLowerCase() !== 'bearer') {
return false;
}
return await JWT_1.JWT.verify(splitted[1]);
}
async parseToken(token) {
if (!(await JWT_1.JWT.verify(token))) {
return false;
}
const decoded = JWT_1.JWT.decode(token);
if (!Array.isArray(decoded.providers)) {
return false;
}
const provider = decoded.providers.filter((data) => data.provider === this.options.name);
if (!provider.length) {
return false;
}
return provider[0];
}
}
exports.SecurityProvider = SecurityProvider;
//# sourceMappingURL=SecurityProvider.js.map