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.737c05ed to 0.1.0-alpha.f9ef5e7f

18

lib/accounts-password.d.ts

@@ -1,3 +0,2 @@

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

@@ -15,19 +14,10 @@ export interface AccountsPasswordOptions {

export default class AccountsPassword {
private serviceName;
private options;
private db;
private server;
constructor(options: AccountsPasswordOptions);
authenticate(params: PasswordLoginType): Promise<UserObjectType>;
findUserByEmail(email: string): Promise<UserObjectType>;
findUserByUsername(username: string): Promise<UserObjectType>;
addEmail(userId: string, newEmail: string, verified: boolean): Promise<void>;
createUser(user: CreateUserType): Promise<string>;
setUsername(userId: string, newUsername: string): Promise<void>;
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);

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

@@ -47,4 +47,2 @@ "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");

@@ -101,169 +99,2 @@ exports.isEmail = function (email) {

};
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, email, emails, token, resetPasswordMail;
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');
}
if (!address) {
email = lodash_1.find(user.emails, function (e) { return !e.verified; });
address = email && email.address;
}
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.email.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: 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.email.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.email.sendMail(enrollmentMail)];
case 3:
_a.sent();
return [2];
}
});
});
};
AccountsPassword.prototype.createUser = function (user) {

@@ -327,2 +158,12 @@ return __awaiter(this, void 0, void 0, function () {

};
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) {

@@ -329,0 +170,0 @@ return __awaiter(this, void 0, void 0, function () {

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

import { PasswordType } from './types';
export declare const bcryptPassword: (password: string) => Promise<string>;
export declare const hashPassword: (password: PasswordType, algorithm: string) => string;
export declare const hashPassword: (password: any, algorithm: string) => any;
export declare const verifyPassword: (password: string, hash: string) => Promise<boolean>;
{
"name": "@accounts/password",
"version": "0.1.0-alpha.737c05ed",
"version": "0.1.0-alpha.f9ef5e7f",
"license": "MIT",
"main": "lib/index.js",
"scripts": {
"clean": "rimraf lib",
"start": "tsc --watch",
"precompile": "npm run clean",
"compile": "tsc",
"prepublish": "npm run compile",
"testonly": "jest --coverage",
"testonly": "jest",
"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": {

@@ -31,4 +17,2 @@ "bcryptjs": "^2.4.3",

"devDependencies": {
"@accounts/common": "^0.1.0-alpha.737c05ed",
"@accounts/server": "^0.1.0-alpha.737c05ed",
"@types/bcryptjs": "^2.4.0",

@@ -38,5 +22,4 @@ "@types/lodash": "^4.14.66"

"peerDependencies": {
"@accounts/common": "^0.1.0-alpha.737c05ed",
"@accounts/server": "^0.1.0-alpha.737c05ed"
"@accounts/common": "^0.1.0"
}
}

@@ -0,31 +1,12 @@

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

@@ -69,6 +50,4 @@ export const isEmail = (email?: string) => {

export default class AccountsPassword {
private serviceName: string;
private options: AccountsPasswordOptions;
private db: DBInterface;
private server: AccountsServer;

@@ -108,231 +87,2 @@ constructor(options: AccountsPasswordOptions) {

/**
* @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> {
const user = await this.db.findUserByEmail(address);
if (!user) {
throw new Error('User not found');
}
// If no address provided find the first unverified email
if (!address) {
const email = find(user.emails, e => !e.verified);
address = email && email.address;
}
// 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.email.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> {
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.email.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.email.sendMail(enrollmentMail);
}
/**
* @description Create a new user.

@@ -342,3 +92,3 @@ * @param user - The user object.

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

@@ -382,4 +132,40 @@ !this.options.validateUsername(user.username) &&

/**
* @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 | LoginUserIdentityType,
user: string | PasswordLoginUserIdentityType,
password: PasswordType

@@ -407,3 +193,2 @@ ): Promise<any> {

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

@@ -410,0 +195,0 @@ if (!hash) {

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

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

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

"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