Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@accounts/password

Package Overview
Dependencies
Maintainers
5
Versions
139
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@accounts/password - npm Package Compare versions

Comparing version 0.1.0-alpha.f9ef5e7f to 0.1.0-alpha.fb3effeb

__tests__/__snapshots__/accounts-password.ts.snap

28

lib/accounts-password.d.ts

@@ -1,2 +0,4 @@

import { CreateUserType, UserObjectType, HashAlgorithm, PasswordLoginType } from '@accounts/common';
import { CreateUserType, UserObjectType, HashAlgorithm } from '@accounts/common';
import { DBInterface, AccountsServer, AuthService } from '@accounts/server';
import { PasswordCreateUserType, PasswordLoginType, PasswordType } from './types';
export declare const isEmail: (email?: string) => boolean;

@@ -10,14 +12,24 @@ export interface AccountsPasswordOptions {

validateEmail?(email?: string): boolean;
validatePassword?(email?: string): boolean;
validateUsername?(email?: string): boolean;
validatePassword?(password?: PasswordType): boolean;
validateUsername?(username?: string): boolean;
}
export default class AccountsPassword {
export default class AccountsPassword implements AuthService {
serviceName: string;
server: AccountsServer;
private options;
private db;
constructor(options: AccountsPasswordOptions);
constructor(options?: AccountsPasswordOptions);
setStore(store: DBInterface): void;
authenticate(params: PasswordLoginType): Promise<UserObjectType>;
createUser(user: CreateUserType): Promise<string>;
setUsername(userId: string, newUsername: string): Promise<void>;
addEmail(userId: string, newEmail: string, verified?: boolean): Promise<void>;
findUserByEmail(email: string): Promise<UserObjectType>;
findUserByUsername(username: string): Promise<UserObjectType>;
addEmail(userId: string, newEmail: string, verified: boolean): Promise<void>;
removeEmail(userId: string, email: string): Promise<void>;
verifyEmail(token: string): Promise<void>;
resetPassword(token: string, newPassword: PasswordType): Promise<void>;
setPassword(userId: string, newPassword: string): Promise<void>;
sendVerificationEmail(address: string): Promise<void>;
sendResetPasswordEmail(address: string): Promise<void>;
sendEnrollmentEmail(address: string): Promise<void>;
createUser(user: PasswordCreateUserType): Promise<string>;
private passwordAuthenticator(user, password);

@@ -24,0 +36,0 @@ private hashAndBcryptPassword(password);

@@ -47,2 +47,4 @@ "use strict";

var lodash_1 = require("lodash");
var server_1 = require("@accounts/server");
var utils_1 = require("@accounts/server/lib/utils");
var encryption_1 = require("./encryption");

@@ -73,4 +75,9 @@ exports.isEmail = function (email) {

function AccountsPassword(options) {
if (options === void 0) { options = {}; }
this.serviceName = 'password';
this.options = __assign({}, defaultOptions, options);
}
AccountsPassword.prototype.setStore = function (store) {
this.db = store;
};
AccountsPassword.prototype.authenticate = function (params) {

@@ -100,2 +107,173 @@ return __awaiter(this, void 0, void 0, function () {

};
AccountsPassword.prototype.findUserByEmail = function (email) {
return this.db.findUserByEmail(email);
};
AccountsPassword.prototype.findUserByUsername = function (username) {
return this.db.findUserByUsername(username);
};
AccountsPassword.prototype.addEmail = function (userId, newEmail, verified) {
return this.db.addEmail(userId, newEmail, verified);
};
AccountsPassword.prototype.removeEmail = function (userId, email) {
return this.db.removeEmail(userId, email);
};
AccountsPassword.prototype.verifyEmail = function (token) {
return __awaiter(this, void 0, void 0, function () {
var user, verificationTokens, tokenRecord, emailRecord;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, this.db.findUserByEmailVerificationToken(token)];
case 1:
user = _a.sent();
if (!user) {
throw new Error('Verify email link expired');
}
verificationTokens = lodash_1.get(user, ['services', 'email', 'verificationTokens'], []);
tokenRecord = lodash_1.find(verificationTokens, function (t) { return t.token === token; });
if (!tokenRecord) {
throw new Error('Verify email link expired');
}
emailRecord = lodash_1.find(user.emails, function (e) { return e.address === tokenRecord.address; });
if (!emailRecord) {
throw new Error('Verify email link is for unknown address');
}
return [4, this.db.verifyEmail(user.id, emailRecord.address)];
case 2:
_a.sent();
return [2];
}
});
});
};
AccountsPassword.prototype.resetPassword = function (token, newPassword) {
return __awaiter(this, void 0, void 0, function () {
var user, resetTokens, resetTokenRecord, emails, password;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, this.db.findUserByResetPasswordToken(token)];
case 1:
user = _a.sent();
if (!user) {
throw new Error('Reset password link expired');
}
resetTokens = lodash_1.get(user, ['services', 'password', 'reset']);
resetTokenRecord = lodash_1.find(resetTokens, function (t) { return t.token === token; });
if (this.server.isTokenExpired(token, resetTokenRecord)) {
throw new Error('Reset password link expired');
}
emails = user.emails || [];
if (!lodash_1.includes(emails.map(function (email) { return email.address; }), resetTokenRecord.address)) {
throw new Error('Token has invalid email address');
}
return [4, this.hashAndBcryptPassword(newPassword)];
case 2:
password = _a.sent();
return [4, this.db.setResetPassword(user.id, resetTokenRecord.address, password, token)];
case 3:
_a.sent();
this.db.invalidateAllSessions(user.id);
return [2];
}
});
});
};
AccountsPassword.prototype.setPassword = function (userId, newPassword) {
return __awaiter(this, void 0, void 0, function () {
var password;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, encryption_1.bcryptPassword(newPassword)];
case 1:
password = _a.sent();
return [2, this.db.setPassword(userId, password)];
}
});
});
};
AccountsPassword.prototype.sendVerificationEmail = function (address) {
return __awaiter(this, void 0, void 0, function () {
var user, emails, token, resetPasswordMail;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!address) {
throw new Error('Invalid email');
}
return [4, this.db.findUserByEmail(address)];
case 1:
user = _a.sent();
if (!user) {
throw new Error('User not found');
}
emails = user.emails || [];
if (!address || !lodash_1.includes(emails.map(function (email) { return email.address; }), address)) {
throw new Error('No such email address for user');
}
token = server_1.generateRandomToken();
return [4, this.db.addEmailVerificationToken(user.id, address, token)];
case 2:
_a.sent();
resetPasswordMail = this.server.prepareMail(address, token, this.server.sanitizeUser(user), 'verify-email', this.server.options.emailTemplates.verifyEmail, this.server.options.emailTemplates.from);
return [4, this.server.options.sendMail(resetPasswordMail)];
case 3:
_a.sent();
return [2];
}
});
});
};
AccountsPassword.prototype.sendResetPasswordEmail = function (address) {
return __awaiter(this, void 0, void 0, function () {
var user, token, resetPasswordMail;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!address) {
throw new Error('Invalid email');
}
return [4, this.db.findUserByEmail(address)];
case 1:
user = _a.sent();
if (!user) {
throw new Error('User not found');
}
address = utils_1.getFirstUserEmail(user, address);
token = server_1.generateRandomToken();
return [4, this.db.addResetPasswordToken(user.id, address, token)];
case 2:
_a.sent();
resetPasswordMail = this.server.prepareMail(address, token, this.server.sanitizeUser(user), 'reset-password', this.server.options.emailTemplates.resetPassword, this.server.options.emailTemplates.from);
return [4, this.server.options.sendMail(resetPasswordMail)];
case 3:
_a.sent();
return [2];
}
});
});
};
AccountsPassword.prototype.sendEnrollmentEmail = function (address) {
return __awaiter(this, void 0, void 0, function () {
var user, token, enrollmentMail;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, this.db.findUserByEmail(address)];
case 1:
user = _a.sent();
if (!user) {
throw new Error('User not found');
}
address = utils_1.getFirstUserEmail(user, address);
token = server_1.generateRandomToken();
return [4, this.db.addResetPasswordToken(user.id, address, token, 'enroll')];
case 2:
_a.sent();
enrollmentMail = this.server.prepareMail(address, token, this.server.sanitizeUser(user), 'enroll-account', this.server.options.emailTemplates.enrollAccount, this.server.options.emailTemplates.from);
return [4, this.server.options.sendMail(enrollmentMail)];
case 3:
_a.sent();
return [2];
}
});
});
};
AccountsPassword.prototype.createUser = function (user) {

@@ -132,2 +310,5 @@ return __awaiter(this, void 0, void 0, function () {

if (!user.password) return [3, 6];
if (!this.options.validatePassword(user.password)) {
throw new Error('Invalid password');
}
return [4, this.hashAndBcryptPassword(user.password)];

@@ -160,12 +341,2 @@ case 5:

};
AccountsPassword.prototype.setUsername = function (userId, newUsername) {
return this.db.setUsername(userId, newUsername);
};
AccountsPassword.prototype.addEmail = function (userId, newEmail, verified) {
if (verified === void 0) { verified = false; }
return this.db.addEmail(userId, newEmail, verified);
};
AccountsPassword.prototype.removeEmail = function (userId, email) {
return this.db.removeEmail(userId, email);
};
AccountsPassword.prototype.passwordAuthenticator = function (user, password) {

@@ -172,0 +343,0 @@ return __awaiter(this, void 0, void 0, function () {

@@ -0,3 +1,4 @@

import { PasswordType } from './types';
export declare const bcryptPassword: (password: string) => Promise<string>;
export declare const hashPassword: (password: any, algorithm: string) => any;
export declare const hashPassword: (password: PasswordType, algorithm: string) => string;
export declare const verifyPassword: (password: string, hash: string) => Promise<boolean>;
import AccountsPassword from './accounts-password';
export default AccountsPassword;
export { AccountsPassword };

@@ -5,2 +5,3 @@ "use strict";

exports.AccountsPassword = accounts_password_1.default;
exports.default = accounts_password_1.default;
//# sourceMappingURL=index.js.map
{
"name": "@accounts/password",
"version": "0.1.0-alpha.f9ef5e7f",
"version": "0.1.0-alpha.fb3effeb",
"license": "MIT",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"clean": "rimraf lib",
"start": "tsc --watch",
"precompile": "npm run clean",
"compile": "tsc",
"testonly": "jest",
"prepublishOnly": "npm run compile",
"test": "npm run test",
"testonly": "jest --coverage",
"coverage": "jest --coverage"
},
"jest": {
"transform": {
".(ts|tsx)": "<rootDir>/../../node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx)$",
"moduleFileExtensions": [
"ts",
"js"
],
"mapCoverage": true
},
"dependencies": {

@@ -17,8 +33,12 @@ "bcryptjs": "^2.4.3",

"devDependencies": {
"@accounts/common": "^0.1.0-alpha.fb3effeb",
"@accounts/server": "^0.1.0-alpha.fb3effeb",
"@types/bcryptjs": "^2.4.0",
"@types/lodash": "^4.14.66"
"@types/lodash": "^4.14.81",
"rimraf": "^2.6.1"
},
"peerDependencies": {
"@accounts/common": "^0.1.0"
"@accounts/common": "^0.1.0",
"@accounts/server": "^0.1.0"
}
}

@@ -1,12 +0,32 @@

import { trim, isEmpty, isFunction, isString, isPlainObject } from 'lodash';
import {
trim,
isEmpty,
isFunction,
isString,
isPlainObject,
get,
find,
includes,
} from 'lodash';
import {
CreateUserType,
UserObjectType,
HashAlgorithm,
PasswordType,
LoginUserIdentityType,
EmailRecord,
TokenRecord,
} from '@accounts/common';
import {
DBInterface,
AccountsServer,
generateRandomToken,
AuthService,
} from '@accounts/server';
import { getFirstUserEmail } from '@accounts/server/lib/utils';
import { hashPassword, bcryptPassword, verifyPassword } from './encryption';
import {
PasswordCreateUserType,
PasswordLoginType,
PasswordLoginUserIdentityType,
} from '@accounts/common';
import { hashPassword, bcryptPassword, verifyPassword } from './encryption';
PasswordType,
} from './types';

@@ -25,4 +45,4 @@ export const isEmail = (email?: string) => {

validateEmail?(email?: string): boolean;
validatePassword?(email?: string): boolean;
validateUsername?(email?: string): boolean;
validatePassword?(password?: PasswordType): boolean;
validateUsername?(username?: string): boolean;
}

@@ -38,3 +58,3 @@

},
validatePassword(password?: string): boolean {
validatePassword(password?: PasswordType): boolean {
const isValid = !isEmpty(password);

@@ -51,10 +71,16 @@ return isValid;

export default class AccountsPassword {
export default class AccountsPassword implements AuthService {
public serviceName = 'password';
public server: AccountsServer;
private options: AccountsPasswordOptions;
private db: DBInterface;
constructor(options: AccountsPasswordOptions) {
constructor(options: AccountsPasswordOptions = {}) {
this.options = { ...defaultOptions, ...options };
}
public setStore(store: DBInterface) {
this.db = store;
}
public async authenticate(

@@ -89,2 +115,232 @@ params: PasswordLoginType

/**
* @description Find a user by one of his emails.
* @param {string} email - User email.
* @returns {Promise<Object>} - Return a user or null if not found.
*/
public findUserByEmail(email: string): Promise<UserObjectType> {
return this.db.findUserByEmail(email);
}
/**
* @description Find a user by his username.
* @param {string} username - User username.
* @returns {Promise<Object>} - Return a user or null if not found.
*/
public findUserByUsername(username: string): Promise<UserObjectType> {
return this.db.findUserByUsername(username);
}
/**
* @description Add an email address for a user.
* Use this instead of directly updating the database.
* @param {string} userId - User id.
* @param {string} newEmail - A new email address for the user.
* @param {boolean} [verified] - Whether the new email address should be marked as verified.
* Defaults to false.
* @returns {Promise<void>} - Return a Promise.
*/
public addEmail(
userId: string,
newEmail: string,
verified: boolean
): Promise<void> {
return this.db.addEmail(userId, newEmail, verified);
}
/**
* @description Remove an email address for a user.
* Use this instead of directly updating the database.
* @param {string} userId - User id.
* @param {string} email - The email address to remove.
* @returns {Promise<void>} - Return a Promise.
*/
public removeEmail(userId: string, email: string): Promise<void> {
return this.db.removeEmail(userId, email);
}
/**
* @description Marks the user's email address as verified.
* @param {string} token - The token retrieved from the verification URL.
* @returns {Promise<void>} - Return a Promise.
*/
public async verifyEmail(token: string): Promise<void> {
const user = await this.db.findUserByEmailVerificationToken(token);
if (!user) {
throw new Error('Verify email link expired');
}
const verificationTokens: TokenRecord[] = get(
user,
['services', 'email', 'verificationTokens'],
[]
);
const tokenRecord = find(
verificationTokens,
(t: TokenRecord) => t.token === token
);
if (!tokenRecord) {
throw new Error('Verify email link expired');
}
// TODO check time for expiry date
const emailRecord = find(
user.emails,
(e: EmailRecord) => e.address === tokenRecord.address
);
if (!emailRecord) {
throw new Error('Verify email link is for unknown address');
}
await this.db.verifyEmail(user.id, emailRecord.address);
}
/**
* @description Reset the password for a user using a token received in email.
* @param {string} token - The token retrieved from the reset password URL.
* @param {string} newPassword - A new password for the user.
* @returns {Promise<void>} - Return a Promise.
*/
public async resetPassword(
token: string,
newPassword: PasswordType
): Promise<void> {
const user = await this.db.findUserByResetPasswordToken(token);
if (!user) {
throw new Error('Reset password link expired');
}
// TODO move this getter into a password service module
const resetTokens = get(user, ['services', 'password', 'reset']);
const resetTokenRecord = find(resetTokens, t => t.token === token);
if (this.server.isTokenExpired(token, resetTokenRecord)) {
throw new Error('Reset password link expired');
}
const emails = user.emails || [];
if (
!includes(
emails.map((email: EmailRecord) => email.address),
resetTokenRecord.address
)
) {
throw new Error('Token has invalid email address');
}
const password = await this.hashAndBcryptPassword(newPassword);
// Change the user password and remove the old token
await this.db.setResetPassword(
user.id,
resetTokenRecord.address,
password,
token
);
// Changing the password should invalidate existing sessions
this.db.invalidateAllSessions(user.id);
}
/**
* @description Change the password for a user.
* @param {string} userId - User id.
* @param {string} newPassword - A new password for the user.
* @returns {Promise<void>} - Return a Promise.
*/
public async setPassword(userId: string, newPassword: string): Promise<void> {
const password = await bcryptPassword(newPassword);
return this.db.setPassword(userId, password);
}
/**
* @description Send an email with a link the user can use verify their email address.
* @param {string} [address] - Which address of the user's to send the email to.
* This address must be in the user's emails list.
* Defaults to the first unverified email in the list.
* @returns {Promise<void>} - Return a Promise.
*/
public async sendVerificationEmail(address: string): Promise<void> {
if (!address) {
throw new Error('Invalid email');
}
const user = await this.db.findUserByEmail(address);
if (!user) {
throw new Error('User not found');
}
// Make sure the address is valid
const emails = user.emails || [];
if (!address || !includes(emails.map(email => email.address), address)) {
throw new Error('No such email address for user');
}
const token = generateRandomToken();
await this.db.addEmailVerificationToken(user.id, address, token);
const resetPasswordMail = this.server.prepareMail(
address,
token,
this.server.sanitizeUser(user),
'verify-email',
this.server.options.emailTemplates.verifyEmail,
this.server.options.emailTemplates.from
);
await this.server.options.sendMail(resetPasswordMail);
}
/**
* @description Send an email with a link the user can use to reset their password.
* @param {string} [address] - Which address of the user's to send the email to.
* This address must be in the user's emails list.
* Defaults to the first email in the list.
* @returns {Promise<void>} - Return a Promise.
*/
public async sendResetPasswordEmail(address: string): Promise<void> {
if (!address) {
throw new Error('Invalid email');
}
const user = await this.db.findUserByEmail(address);
if (!user) {
throw new Error('User not found');
}
address = getFirstUserEmail(user, address);
const token = generateRandomToken();
await this.db.addResetPasswordToken(user.id, address, token);
const resetPasswordMail = this.server.prepareMail(
address,
token,
this.server.sanitizeUser(user),
'reset-password',
this.server.options.emailTemplates.resetPassword,
this.server.options.emailTemplates.from
);
await this.server.options.sendMail(resetPasswordMail);
}
/**
* @description Send an email with a link the user can use to set their initial password.
* @param {string} [address] - Which address of the user's to send the email to.
* This address must be in the user's emails list.
* Defaults to the first email in the list.
* @returns {Promise<void>} - Return a Promise.
*/
public async sendEnrollmentEmail(address: string): Promise<void> {
const user = await this.db.findUserByEmail(address);
if (!user) {
throw new Error('User not found');
}
address = getFirstUserEmail(user, address);
const token = generateRandomToken();
await this.db.addResetPasswordToken(user.id, address, token, 'enroll');
const enrollmentMail = this.server.prepareMail(
address,
token,
this.server.sanitizeUser(user),
'enroll-account',
this.server.options.emailTemplates.enrollAccount,
this.server.options.emailTemplates.from
);
await this.server.options.sendMail(enrollmentMail);
}
/**
* @description Create a new user.

@@ -94,3 +350,3 @@ * @param user - The user object.

*/
public async createUser(user: CreateUserType): Promise<string> {
public async createUser(user: PasswordCreateUserType): Promise<string> {
if (

@@ -113,2 +369,5 @@ !this.options.validateUsername(user.username) &&

if (user.password) {
if (!this.options.validatePassword(user.password)) {
throw new Error('Invalid password');
}
password = await this.hashAndBcryptPassword(user.password);

@@ -135,40 +394,4 @@ }

/**
* @description Change a user's username.
* Use this instead of directly updating the database.
* @param userId - User id.
* @param newUsername - A new email address for the user.
*/
public setUsername(userId: string, newUsername: string): Promise<void> {
return this.db.setUsername(userId, newUsername);
}
/**
* @description Add an email address for a user.
* Use this instead of directly updating the database.
* @param userId - User id.
* @param newEmail - A new email address for the user.
* @param [verified=false] - Whether the new email address should be marked as verified.
* Defaults to false.
*/
public addEmail(
userId: string,
newEmail: string,
verified: boolean = false
): Promise<void> {
return this.db.addEmail(userId, newEmail, verified);
}
/**
* @description Remove an email address for a user.
* Use this instead of directly updating the database.
* @param userId - User id.
* @param email - The email address to remove.
*/
public removeEmail(userId: string, email: string): Promise<void> {
return this.db.removeEmail(userId, email);
}
private async passwordAuthenticator(
user: string | PasswordLoginUserIdentityType,
user: string | LoginUserIdentityType,
password: PasswordType

@@ -196,2 +419,3 @@ ): Promise<any> {

}
const hash = await this.db.findPasswordHash(foundUser.id);

@@ -198,0 +422,0 @@ if (!hash) {

import * as bcrypt from 'bcryptjs';
import * as crypto from 'crypto';
import { PasswordType } from '@accounts/common';
import { PasswordType } from './types';

@@ -5,0 +5,0 @@ export const bcryptPassword = async (password: string): Promise<string> => {

import AccountsPassword from './accounts-password';
export default AccountsPassword;
export { AccountsPassword };

@@ -6,3 +6,8 @@ {

"outDir": "./lib"
}
},
"exclude": [
"node_modules",
"__tests__",
"lib"
]
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc