@accounts/password
Advanced tools
Comparing version 0.1.0-beta.5 to 0.1.0-beta.6
@@ -34,8 +34,6 @@ import { set } from 'lodash'; | ||
const user = { | ||
services: {} | ||
services: {}, | ||
}; | ||
const tmpAccountsPassword = new AccountsPassword({}); | ||
tmpAccountsPassword.passwordAuthenticator = jest.fn(() => | ||
Promise.resolve(user) | ||
); | ||
tmpAccountsPassword.passwordAuthenticator = jest.fn(() => Promise.resolve(user)); | ||
const ret = await tmpAccountsPassword.authenticate({ | ||
@@ -135,5 +133,3 @@ user: 'toto', | ||
const email = 'john.doe@gmail.com'; | ||
set(validUser, 'services.email.verificationTokens', [ | ||
{ token, address: email }, | ||
]); | ||
set(validUser, 'services.email.verificationTokens', [{ token, address: email }]); | ||
validUser.emails = [{ address: email }]; | ||
@@ -155,5 +151,3 @@ const invalidUser = { ...validUser }; | ||
it('throws when token not found', async () => { | ||
const findUserByEmailVerificationToken = jest.fn(() => | ||
Promise.resolve({}) | ||
); | ||
const findUserByEmailVerificationToken = jest.fn(() => Promise.resolve({})); | ||
password.setStore({ findUserByEmailVerificationToken } as any); | ||
@@ -169,5 +163,3 @@ try { | ||
it('throws when email not found', async () => { | ||
const findUserByEmailVerificationToken = jest.fn(() => | ||
Promise.resolve(invalidUser) | ||
); | ||
const findUserByEmailVerificationToken = jest.fn(() => Promise.resolve(invalidUser)); | ||
password.setStore({ findUserByEmailVerificationToken } as any); | ||
@@ -183,5 +175,3 @@ try { | ||
it('call this.db.verifyEmail', async () => { | ||
const findUserByEmailVerificationToken = jest.fn(() => | ||
Promise.resolve(validUser) | ||
); | ||
const findUserByEmailVerificationToken = jest.fn(() => Promise.resolve(validUser)); | ||
const verifyEmail = jest.fn(() => Promise.resolve()); | ||
@@ -221,5 +211,3 @@ password.setStore({ | ||
it('throws when token is expired', async () => { | ||
const findUserByResetPasswordToken = jest.fn(() => | ||
Promise.resolve(invalidUser) | ||
); | ||
const findUserByResetPasswordToken = jest.fn(() => Promise.resolve(invalidUser)); | ||
const isTokenExpired = jest.fn(() => true); | ||
@@ -237,5 +225,3 @@ password.setStore({ findUserByResetPasswordToken } as any); | ||
it('throws when token have invalid email', async () => { | ||
const findUserByResetPasswordToken = jest.fn(() => | ||
Promise.resolve(invalidUser) | ||
); | ||
const findUserByResetPasswordToken = jest.fn(() => Promise.resolve(invalidUser)); | ||
const isTokenExpired = jest.fn(() => false); | ||
@@ -253,5 +239,3 @@ password.setStore({ findUserByResetPasswordToken } as any); | ||
it('reset password and invalidate all sessions', async () => { | ||
const findUserByResetPasswordToken = jest.fn(() => | ||
Promise.resolve(validUser) | ||
); | ||
const findUserByResetPasswordToken = jest.fn(() => Promise.resolve(validUser)); | ||
const isTokenExpired = jest.fn(() => false); | ||
@@ -258,0 +242,0 @@ const setResetPassword = jest.fn(() => Promise.resolve()); |
@@ -1,6 +0,8 @@ | ||
import { CreateUserType, UserObjectType, HashAlgorithm } from '@accounts/common'; | ||
import { DBInterface, AccountsServer, AuthService } from '@accounts/server'; | ||
import { CreateUser, User, DatabaseInterface, AuthenticationService } from '@accounts/types'; | ||
import { HashAlgorithm } from '@accounts/common'; | ||
import { TwoFactor, AccountsTwoFactorOptions } from '@accounts/two-factor'; | ||
import { PasswordCreateUserType, PasswordLoginType, PasswordType } from './types'; | ||
export declare const isEmail: (email?: string) => boolean; | ||
import { AccountsServer } from '@accounts/server'; | ||
import { PasswordCreateUserType } from './types/password-create-user-type'; | ||
import { PasswordLoginType } from './types/password-login-type'; | ||
import { PasswordType } from './types/password-type'; | ||
export interface AccountsPasswordOptions { | ||
@@ -12,3 +14,3 @@ twoFactor?: AccountsTwoFactorOptions; | ||
minimumPasswordLength?: number; | ||
validateNewUser?: (user: CreateUserType) => Promise<boolean>; | ||
validateNewUser?: (user: CreateUser) => Promise<boolean>; | ||
validateEmail?(email?: string): boolean; | ||
@@ -18,3 +20,3 @@ validatePassword?(password?: PasswordType): boolean; | ||
} | ||
export default class AccountsPassword implements AuthService { | ||
export default class AccountsPassword implements AuthenticationService { | ||
serviceName: string; | ||
@@ -26,6 +28,6 @@ server: AccountsServer; | ||
constructor(options?: AccountsPasswordOptions); | ||
setStore(store: DBInterface): void; | ||
authenticate(params: PasswordLoginType): Promise<UserObjectType>; | ||
findUserByEmail(email: string): Promise<UserObjectType | null>; | ||
findUserByUsername(username: string): Promise<UserObjectType | null>; | ||
setStore(store: DatabaseInterface): void; | ||
authenticate(params: PasswordLoginType): Promise<User>; | ||
findUserByEmail(email: string): Promise<User | null>; | ||
findUserByUsername(username: string): Promise<User | null>; | ||
addEmail(userId: string, newEmail: string, verified: boolean): Promise<void>; | ||
@@ -32,0 +34,0 @@ removeEmail(userId: string, email: string): Promise<void>; |
@@ -47,10 +47,6 @@ "use strict"; | ||
var lodash_1 = require("lodash"); | ||
var two_factor_1 = require("@accounts/two-factor"); | ||
var server_1 = require("@accounts/server"); | ||
var two_factor_1 = require("@accounts/two-factor"); | ||
var utils_1 = require("@accounts/server/lib/utils"); | ||
var encryption_1 = require("./encryption"); | ||
exports.isEmail = function (email) { | ||
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; | ||
return email && re.test(email); | ||
}; | ||
var encryption_1 = require("./utils/encryption"); | ||
var isEmail_1 = require("./utils/isEmail"); | ||
var defaultOptions = { | ||
@@ -61,3 +57,3 @@ passwordResetTokenExpirationInDays: 3, | ||
validateEmail: function (email) { | ||
var isValid = !lodash_1.isEmpty(lodash_1.trim(email || '')) && exports.isEmail(email); | ||
var isValid = !lodash_1.isEmpty(lodash_1.trim(email || '')) && isEmail_1.isEmail(email); | ||
return Boolean(isValid); | ||
@@ -244,3 +240,3 @@ }, | ||
} | ||
address = utils_1.getFirstUserEmail(user, address); | ||
address = server_1.getFirstUserEmail(user, address); | ||
token = server_1.generateRandomToken(); | ||
@@ -270,3 +266,3 @@ return [4, this.db.addResetPasswordToken(user.id, address, token)]; | ||
} | ||
address = utils_1.getFirstUserEmail(user, address); | ||
address = server_1.getFirstUserEmail(user, address); | ||
token = server_1.generateRandomToken(); | ||
@@ -291,4 +287,3 @@ return [4, this.db.addResetPasswordToken(user.id, address, token, 'enroll')]; | ||
case 0: | ||
if (!this.options.validateUsername(user.username) && | ||
!this.options.validateEmail(user.email)) { | ||
if (!this.options.validateUsername(user.username) && !this.options.validateEmail(user.email)) { | ||
throw new Error('Username or Email is required'); | ||
@@ -384,5 +379,3 @@ } | ||
hashAlgorithm = this.options.passwordHashAlgorithm; | ||
pass = hashAlgorithm | ||
? encryption_1.hashPassword(password, hashAlgorithm) | ||
: password; | ||
pass = hashAlgorithm ? encryption_1.hashPassword(password, hashAlgorithm) : password; | ||
return [4, encryption_1.verifyPassword(pass, hash)]; | ||
@@ -404,5 +397,3 @@ case 8: | ||
hashAlgorithm = this.options.passwordHashAlgorithm; | ||
hashedPassword = hashAlgorithm | ||
? encryption_1.hashPassword(password, hashAlgorithm) | ||
: password; | ||
hashedPassword = hashAlgorithm ? encryption_1.hashPassword(password, hashAlgorithm) : password; | ||
return [2, encryption_1.bcryptPassword(hashedPassword)]; | ||
@@ -415,3 +406,3 @@ }); | ||
if (user && !username && !email) { | ||
if (exports.isEmail(user)) { | ||
if (isEmail_1.isEmail(user)) { | ||
email = user; | ||
@@ -418,0 +409,0 @@ username = null; |
{ | ||
"name": "@accounts/password", | ||
"version": "0.1.0-beta.5", | ||
"version": "0.1.0-beta.6", | ||
"license": "MIT", | ||
@@ -28,3 +28,3 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"@accounts/two-factor": "^0.1.0-beta.5", | ||
"@accounts/two-factor": "^0.1.0-beta.6", | ||
"bcryptjs": "^2.4.3", | ||
@@ -34,6 +34,7 @@ "lodash": "^4.17.4" | ||
"devDependencies": { | ||
"@accounts/common": "^0.1.0-beta.5", | ||
"@accounts/server": "^0.1.0-beta.5", | ||
"@accounts/common": "^0.1.0-beta.6", | ||
"@accounts/server": "^0.1.0-beta.6", | ||
"@accounts/types": "^0.1.0-beta.6", | ||
"@types/bcryptjs": "2.4.1", | ||
"@types/lodash": "4.14.104", | ||
"@types/lodash": "4.14.105", | ||
"rimraf": "2.6.2" | ||
@@ -40,0 +41,0 @@ }, |
@@ -1,39 +0,14 @@ | ||
import { | ||
trim, | ||
isEmpty, | ||
isFunction, | ||
isString, | ||
isPlainObject, | ||
get, | ||
find, | ||
includes, | ||
} from 'lodash'; | ||
import { | ||
CreateUserType, | ||
UserObjectType, | ||
HashAlgorithm, | ||
LoginUserIdentityType, | ||
EmailRecord, | ||
TokenRecord, | ||
} from '@accounts/common'; | ||
import { | ||
DBInterface, | ||
AccountsServer, | ||
generateRandomToken, | ||
AuthService, | ||
} from '@accounts/server'; | ||
import { trim, isEmpty, isFunction, isString, isPlainObject, get, find, includes } from 'lodash'; | ||
import { CreateUser, User, Login, EmailRecord, TokenRecord, DatabaseInterface, AuthenticationService } from '@accounts/types'; | ||
import { HashAlgorithm } from '@accounts/common'; | ||
import { TwoFactor, AccountsTwoFactorOptions } from '@accounts/two-factor'; | ||
import { getFirstUserEmail } from '@accounts/server/lib/utils'; | ||
import { hashPassword, bcryptPassword, verifyPassword } from './encryption'; | ||
import { | ||
PasswordCreateUserType, | ||
PasswordLoginType, | ||
PasswordType, | ||
} from './types'; | ||
import { AccountsServer, generateRandomToken, getFirstUserEmail } from '@accounts/server'; | ||
import { hashPassword, bcryptPassword, verifyPassword } from './utils/encryption'; | ||
export const isEmail = (email?: string) => { | ||
const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; | ||
return email && re.test(email); | ||
}; | ||
import { PasswordCreateUserType } from './types/password-create-user-type'; | ||
import { PasswordLoginType } from './types/password-login-type'; | ||
import { PasswordType } from './types/password-type'; | ||
import { isEmail } from './utils/isEmail'; | ||
export interface AccountsPasswordOptions { | ||
@@ -45,3 +20,3 @@ twoFactor?: AccountsTwoFactorOptions; | ||
minimumPasswordLength?: number; | ||
validateNewUser?: (user: CreateUserType) => Promise<boolean>; | ||
validateNewUser?: (user: CreateUser) => Promise<boolean>; | ||
validateEmail?(email?: string): boolean; | ||
@@ -66,4 +41,3 @@ validatePassword?(password?: PasswordType): boolean; | ||
const usernameRegex = /^[a-zA-Z][a-zA-Z0-9]*$/; | ||
const isValid = | ||
username && !isEmpty(trim(username)) && usernameRegex.test(username); | ||
const isValid = username && !isEmpty(trim(username)) && usernameRegex.test(username); | ||
return Boolean(isValid); | ||
@@ -73,3 +47,3 @@ }, | ||
export default class AccountsPassword implements AuthService { | ||
export default class AccountsPassword implements AuthenticationService { | ||
public serviceName = 'password'; | ||
@@ -79,3 +53,3 @@ public server: AccountsServer; | ||
private options: AccountsPasswordOptions; | ||
private db: DBInterface; | ||
private db: DatabaseInterface; | ||
@@ -87,3 +61,3 @@ constructor(options: AccountsPasswordOptions = {}) { | ||
public setStore(store: DBInterface) { | ||
public setStore(store: DatabaseInterface) { | ||
this.db = store; | ||
@@ -93,5 +67,3 @@ this.twoFactor.setStore(store); | ||
public async authenticate( | ||
params: PasswordLoginType | ||
): Promise<UserObjectType> { | ||
public async authenticate(params: PasswordLoginType): Promise<User> { | ||
const { user, password, code } = params; | ||
@@ -120,3 +92,3 @@ if (!user || !password) { | ||
*/ | ||
public findUserByEmail(email: string): Promise<UserObjectType | null> { | ||
public findUserByEmail(email: string): Promise<User | null> { | ||
return this.db.findUserByEmail(email); | ||
@@ -130,3 +102,3 @@ } | ||
*/ | ||
public findUserByUsername(username: string): Promise<UserObjectType | null> { | ||
public findUserByUsername(username: string): Promise<User | null> { | ||
return this.db.findUserByUsername(username); | ||
@@ -144,7 +116,3 @@ } | ||
*/ | ||
public addEmail( | ||
userId: string, | ||
newEmail: string, | ||
verified: boolean | ||
): Promise<void> { | ||
public addEmail(userId: string, newEmail: string, verified: boolean): Promise<void> { | ||
// TODO use this.options.verifyEmail before | ||
@@ -181,6 +149,3 @@ return this.db.addEmail(userId, newEmail, verified); | ||
); | ||
const tokenRecord = find( | ||
verificationTokens, | ||
(t: TokenRecord) => t.token === token | ||
); | ||
const tokenRecord = find(verificationTokens, (t: TokenRecord) => t.token === token); | ||
if (!tokenRecord) { | ||
@@ -190,6 +155,3 @@ throw new Error('Verify email link expired'); | ||
// TODO check time for expiry date | ||
const emailRecord = find( | ||
user.emails, | ||
(e: EmailRecord) => e.address === tokenRecord.address | ||
); | ||
const emailRecord = find(user.emails, (e: EmailRecord) => e.address === tokenRecord.address); | ||
if (!emailRecord) { | ||
@@ -207,6 +169,3 @@ throw new Error('Verify email link is for unknown address'); | ||
*/ | ||
public async resetPassword( | ||
token: string, | ||
newPassword: PasswordType | ||
): Promise<void> { | ||
public async resetPassword(token: string, newPassword: PasswordType): Promise<void> { | ||
const user = await this.db.findUserByResetPasswordToken(token); | ||
@@ -226,8 +185,3 @@ if (!user) { | ||
const emails = user.emails || []; | ||
if ( | ||
!includes( | ||
emails.map((email: EmailRecord) => email.address), | ||
resetTokenRecord.address | ||
) | ||
) { | ||
if (!includes(emails.map((email: EmailRecord) => email.address), resetTokenRecord.address)) { | ||
throw new Error('Token has invalid email address'); | ||
@@ -238,8 +192,3 @@ } | ||
// Change the user password and remove the old token | ||
await this.db.setResetPassword( | ||
user.id, | ||
resetTokenRecord.address, | ||
password, | ||
token | ||
); | ||
await this.db.setResetPassword(user.id, resetTokenRecord.address, password, token); | ||
// Changing the password should invalidate existing sessions | ||
@@ -360,6 +309,3 @@ this.db.invalidateAllSessions(user.id); | ||
public async createUser(user: PasswordCreateUserType): Promise<string> { | ||
if ( | ||
!this.options.validateUsername(user.username) && | ||
!this.options.validateEmail(user.email) | ||
) { | ||
if (!this.options.validateUsername(user.username) && !this.options.validateEmail(user.email)) { | ||
throw new Error('Username or Email is required'); | ||
@@ -392,6 +338,3 @@ } | ||
const { validateNewUser } = this.options; | ||
if ( | ||
isFunction(validateNewUser) && | ||
!await validateNewUser(proposedUserObject) | ||
) { | ||
if (isFunction(validateNewUser) && !await validateNewUser(proposedUserObject)) { | ||
throw new Error('User invalid'); | ||
@@ -404,5 +347,5 @@ } | ||
private async passwordAuthenticator( | ||
user: string | LoginUserIdentityType, | ||
user: string | Login, | ||
password: PasswordType | ||
): Promise<UserObjectType> { | ||
): Promise<User> { | ||
const { username, email, id } = isString(user) | ||
@@ -412,3 +355,3 @@ ? this.toUsernameAndEmail({ user }) | ||
let foundUser: UserObjectType; | ||
let foundUser: User; | ||
@@ -436,5 +379,3 @@ if (id) { | ||
const hashAlgorithm = this.options.passwordHashAlgorithm; | ||
const pass: any = hashAlgorithm | ||
? hashPassword(password, hashAlgorithm) | ||
: password; | ||
const pass: any = hashAlgorithm ? hashPassword(password, hashAlgorithm) : password; | ||
const isPasswordValid = await verifyPassword(pass, hash); | ||
@@ -451,5 +392,3 @@ | ||
const hashAlgorithm = this.options.passwordHashAlgorithm; | ||
const hashedPassword: any = hashAlgorithm | ||
? hashPassword(password, hashAlgorithm) | ||
: password; | ||
const hashedPassword: any = hashAlgorithm ? hashPassword(password, hashAlgorithm) : password; | ||
return bcryptPassword(hashedPassword); | ||
@@ -456,0 +395,0 @@ } |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
208894
46
6
1995
+ Added@types/node@22.10.0(transitive)
+ Addedundici-types@6.20.0(transitive)
- Removed@types/node@22.9.3(transitive)
- Removedundici-types@6.19.8(transitive)