@krypton-org/krypton-auth
Version:
Express authentication middleware, using GraphQL and JSON Web Tokens.
198 lines • 8.14 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Module returning the Mongoose model built with the UserSchema importer from {@link module:Model/UserSchema}
* @module model/UserModel
*/
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const mongoose_1 = require("mongoose");
const config_1 = __importDefault(require("../config"));
const PasswordEncryption = __importStar(require("../crypto/PasswordEncryption"));
const ErrorTypes_1 = require("../error/ErrorTypes");
const UserSchema_1 = require("./UserSchema");
/**
* Compute users JsonWebTokens encoding the `user` data with the `privateKey`. This authentication token will be valid for `expiresIn` number of milliseconds.
* @throws {TokenEncryptionError}
* @param {any} user
* @param {string} privateKey
* @param {number} expiresIn
* @returns {Promise<string>} Promise to the token.
*/
const computeUserToken = (user, privateKey, expiresIn) => {
return new Promise((resolve, reject) => {
jsonwebtoken_1.default.sign(user, privateKey, { expiresIn: expiresIn + 'ms', algorithm: config_1.default.algorithm }, (err, token) => __awaiter(void 0, void 0, void 0, function* () {
if (err) {
reject(new ErrorTypes_1.TokenEncryptionError(err.message));
}
else {
resolve(token);
}
}));
});
};
const noninternalFieldsFilter = '-' + UserSchema_1.internalFields.join(' -');
const internalFieldsMap = new Map();
UserSchema_1.internalFields.map(x => internalFieldsMap.set(x, true));
const User = new mongoose_1.Schema(UserSchema_1.UserSchema);
/** @see {@link IUserModel#userExists} */
User.statics.userExists = function (filter) {
return this.findOne(filter).then(result => !!result);
};
/** @see {@link IUserModel#getUser} */
User.statics.getUser = function (filter) {
return this.findOne(filter).then(user => {
if (user == null) {
throw new ErrorTypes_1.UnauthorizedError('User not found.');
}
return user;
});
};
/** @see {@link IUserModel#getUserNonInternalFields} */
User.statics.getUserNonInternalFields = function (filter) {
return this.findOne(filter)
.select(noninternalFieldsFilter)
.lean()
.then(user => {
if (user == null) {
throw new ErrorTypes_1.UnauthorizedError('User not found.');
}
return user;
});
};
/** @see {@link IUserModel#createUser} */
User.statics.createUser = function (data) {
return __awaiter(this, void 0, void 0, function* () {
const UserInstance = mongoose_1.model('user', User);
return PasswordEncryption.hashAndSalt(data.password).then(results => {
const user = new UserInstance();
Object.keys(data).map(prop => {
if (prop !== 'password') {
if (internalFieldsMap.has(prop)) {
user[prop] = data[prop];
}
else {
user[prop] = escapeHtml(data[prop]);
}
}
});
user.password = results.hash;
user.passwordSalt = results.salt;
return user.save();
}, err => {
throw new ErrorTypes_1.EncryptionFailedError('Password encryption failed :' + err);
});
});
};
/** @see {@link IUserModel#isPasswordValid} */
User.statics.isPasswordValid = function (filter, password) {
let user;
return this.getUser(filter)
.then(userFound => {
user = userFound;
return PasswordEncryption.hash(password, user.passwordSalt);
})
.then(results => {
return results.hash === user.password;
});
};
/** @see {@link IUserModel#sign} */
User.statics.sign = function (filter, password, privateKey) {
return __awaiter(this, void 0, void 0, function* () {
const isPasswordValid = yield this.isPasswordValid(filter, password);
if (!isPasswordValid) {
throw new ErrorTypes_1.UserNotFoundError('Wrong credentials.');
}
const user = yield this.getUserNonInternalFields(filter);
const expiryDate = new Date();
expiryDate.setTime(expiryDate.getTime() + config_1.default.authTokenExpiryTime);
const token = yield computeUserToken(user, privateKey, config_1.default.authTokenExpiryTime);
return { user, token, expiryDate };
});
};
/** @see {@link IUserModel#refreshAuthToken} */
User.statics.refreshAuthToken = function (filter, privateKey) {
return __awaiter(this, void 0, void 0, function* () {
const user = yield this.getUserNonInternalFields(filter);
const expiryDate = new Date();
expiryDate.setTime(expiryDate.getTime() + config_1.default.authTokenExpiryTime);
const token = yield computeUserToken(user, privateKey, config_1.default.authTokenExpiryTime);
return { expiryDate, token, user };
});
};
/** @see {@link IUserModel#verify} */
User.statics.verify = function (token, publicKey) {
return new Promise((resolve, reject) => {
jsonwebtoken_1.default.verify(token, publicKey, { algorithms: [config_1.default.algorithm] }, (err, userDecrypted) => __awaiter(this, void 0, void 0, function* () {
if (err) {
reject(new ErrorTypes_1.UnauthorizedError('User not found, please log in.'));
}
else {
resolve(userDecrypted);
}
}));
});
};
/** @see {@link IUserModel#updateUser} */
User.statics.updateUser = function (filter, data) {
return __awaiter(this, void 0, void 0, function* () {
if (data.password) {
try {
const results = yield PasswordEncryption.hashAndSalt(data.password);
data = Object.assign(Object.assign({}, data), { password: results.hash, passwordSalt: results.salt });
}
catch (err) {
throw new ErrorTypes_1.EncryptionFailedError('Password encryption failed :' + err);
}
}
Object.keys(data).map(prop => {
if (!internalFieldsMap.has(prop)) {
data[prop] = escapeHtml(data[prop]);
}
});
yield this.updateOne(filter, data, { runValidators: true });
});
};
/** @see {@link IUserModel#removeUser} */
User.statics.removeUser = function (filter) {
return this.deleteOne(filter);
};
/**
* Escape unsafe string from HTML injection to protect users from XSS attacks.
* @param {string} unsafe
* @returns {string} Safe string
*/
function escapeHtml(unsafe) {
if (typeof unsafe !== 'string') {
return unsafe;
}
else {
return unsafe
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
}
const UserModel = mongoose_1.model('User', User);
exports.default = UserModel;
//# sourceMappingURL=UserModel.js.map