diff --git a/packages/core/__e2e__/monographs.test.js b/packages/core/__e2e__/monographs.test.js index 91e5159a3..8582e6cbd 100644 --- a/packages/core/__e2e__/monographs.test.js +++ b/packages/core/__e2e__/monographs.test.js @@ -18,12 +18,7 @@ along with this program. If not, see . */ import { databaseTest, noteTest, StorageInterface } from "../__tests__/utils"; - -const user = { - email: process.env.EMAIL, - password: process.env.PASSWORD, - hashedPassword: process.env.HASHED_PASSWORD -}; +import { login } from "./utils"; jest.setTimeout(15 * 1000); @@ -53,7 +48,7 @@ afterAll(async () => { test("publish a monograph", () => noteTest().then(async ({ db, id }) => { - await db.user.login(user.email, user.password, user.hashedPassword); + await login(db); await db.monographs.init(); const monographId = await db.monographs.publish(id); @@ -68,7 +63,7 @@ test("publish a monograph", () => test("update a published monograph", () => noteTest().then(async ({ db, id }) => { - await db.user.login(user.email, user.password, user.hashedPassword); + await login(db); await db.monographs.init(); const monographId = await db.monographs.publish(id); @@ -85,7 +80,7 @@ test("update a published monograph", () => test("unpublish a monograph", () => noteTest().then(async ({ db, id }) => { - await db.user.login(user.email, user.password, user.hashedPassword); + await login(db); await db.monographs.init(); await db.monographs.publish(id); diff --git a/packages/core/__e2e__/token-manager.test.js b/packages/core/__e2e__/token-manager.test.js index a101b0ed0..3246a1dca 100644 --- a/packages/core/__e2e__/token-manager.test.js +++ b/packages/core/__e2e__/token-manager.test.js @@ -19,6 +19,7 @@ along with this program. If not, see . import DB from "../api"; import StorageInterface from "../__mocks__/storage.mock"; +import { login } from "./utils"; const user = { email: process.env.EMAIL, @@ -30,9 +31,7 @@ test("refresh token concurrently", async () => { const db = new DB(StorageInterface); await db.init(); - await expect( - db.user.login(user.email, user.password, user.hashedPassword) - ).resolves.not.toThrow(); + await expect(login(db)).resolves.not.toThrow(); const token = await db.user.tokenManager.getToken(); expect(token).toBeDefined(); @@ -51,9 +50,7 @@ test("refresh token using the same refresh_token multiple time", async () => { const db = new DB(StorageInterface); await db.init(); - await expect( - db.user.login(user.email, user.password, user.hashedPassword) - ).resolves.not.toThrow(); + await expect(login(db)).resolves.not.toThrow(); const token = await db.user.tokenManager.getToken(); expect(token).toBeDefined(); diff --git a/packages/core/__e2e__/user-manager.test.js b/packages/core/__e2e__/user-manager.test.js index df28ada2f..ea1d47a86 100644 --- a/packages/core/__e2e__/user-manager.test.js +++ b/packages/core/__e2e__/user-manager.test.js @@ -18,12 +18,7 @@ along with this program. If not, see . */ import { databaseTest } from "../__tests__/utils"; - -const user = { - email: process.env.EMAIL, - password: process.env.PASSWORD, - hashed: process.env.HASHED_PASSWORD -}; +import { login, user } from "./utils"; // test("signup user and check for token", async () => { // const db = new DB(StorageInterface); @@ -40,9 +35,7 @@ test( "login user and check for token", () => databaseTest().then(async (db) => { - await expect( - db.user.login(user.email, user.password, user.hashed) - ).resolves.not.toThrow(); + await expect(login(db)).resolves.not.toThrow(); await expect(db.user.tokenManager.getToken()).resolves.toBeDefined(); }), @@ -53,7 +46,7 @@ test( "login user and get user data", () => databaseTest().then(async (db) => { - await db.user.login(user.email, user.password, user.hashed); + await login(db); const userData = await db.user.getUser(); expect(userData.email).toBe(user.email); @@ -65,7 +58,7 @@ test( "login user and logout user", () => databaseTest().then(async (db) => { - await db.user.login(user.email, user.password, user.hashed); + await login(db); await expect(db.user.logout()).resolves.not.toThrow(); }), diff --git a/packages/core/__e2e__/utils.js b/packages/core/__e2e__/utils.js new file mode 100644 index 000000000..78821dccf --- /dev/null +++ b/packages/core/__e2e__/utils.js @@ -0,0 +1,36 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2022 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { authenticator } from "otplib"; + +export const user = { + email: process.env.EMAIL, + password: process.env.PASSWORD, + hashed: process.env.HASHED_PASSWORD, + totpSecret: process.env.TOTP_SECRET +}; + +export async function login(db) { + await db.user.authenticateEmail(user.email); + + const token = authenticator.generate(user.totpSecret); + await db.user.authenticateMultiFactorCode(token, "app"); + + await db.user.authenticatePassword(user.email, user.password, user.hashed); +} diff --git a/packages/core/api/user-manager.js b/packages/core/api/user-manager.js index 9ab795926..bf2ddb7ef 100644 --- a/packages/core/api/user-manager.js +++ b/packages/core/api/user-manager.js @@ -147,51 +147,6 @@ class UserManager { EV.publish(EVENTS.userLoggedIn, user); } - /** - * @deprecated Please use `authenticateEmail`, `authenticateMultiFactorCode` & `authenticatePassword` for login - */ - async login(email, password, hashedPassword = undefined) { - return this._login({ email, password, hashedPassword }); - } - - /** - * @deprecated Please use `authenticateEmail`, `authenticateMultiFactorCode` & `authenticatePassword` for login - */ - async mfaLogin(email, password, { code, method }) { - return this._login({ email, password, code, method }); - } - - /** - * @private - */ - async _login({ email, password, hashedPassword, code, method }) { - email = email && email.toLowerCase(); - - if (!hashedPassword && password) { - hashedPassword = await this._storage.hash(password, email); - } - - await this.tokenManager.saveToken( - await http.post(`${constants.AUTH_HOST}${ENDPOINTS.token}`, { - username: email, - password: hashedPassword, - grant_type: code ? "mfa" : "password", - scope: "notesnook.sync offline_access openid IdentityServerApi", - client_id: "notesnook", - "mfa:code": code, - "mfa:method": method - }) - ); - - const user = await this.fetchUser(); - await this._storage.deriveCryptoKey(`_uk_@${user.email}`, { - password, - salt: user.salt - }); - - EV.publish(EVENTS.userLoggedIn, user); - } - async getSessions() { const token = await this.tokenManager.getAccessToken(); if (!token) return; diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index d53576737..b7c8a3c12 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -41,7 +41,8 @@ "jest": "^28.1.3", "jest-fetch-mock": "^3.0.3", "jsdom": "^20.0.0", - "mockdate": "^3.0.5" + "mockdate": "^3.0.5", + "otplib": "^12.0.1" } }, "node_modules/@ampproject/remapping": { @@ -2520,6 +2521,53 @@ "node": ">= 10" } }, + "node_modules/@otplib/core": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz", + "integrity": "sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==", + "dev": true + }, + "node_modules/@otplib/plugin-crypto": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz", + "integrity": "sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==", + "dev": true, + "dependencies": { + "@otplib/core": "^12.0.1" + } + }, + "node_modules/@otplib/plugin-thirty-two": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz", + "integrity": "sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==", + "dev": true, + "dependencies": { + "@otplib/core": "^12.0.1", + "thirty-two": "^1.0.2" + } + }, + "node_modules/@otplib/preset-default": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/preset-default/-/preset-default-12.0.1.tgz", + "integrity": "sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==", + "dev": true, + "dependencies": { + "@otplib/core": "^12.0.1", + "@otplib/plugin-crypto": "^12.0.1", + "@otplib/plugin-thirty-two": "^12.0.1" + } + }, + "node_modules/@otplib/preset-v11": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/preset-v11/-/preset-v11-12.0.1.tgz", + "integrity": "sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==", + "dev": true, + "dependencies": { + "@otplib/core": "^12.0.1", + "@otplib/plugin-crypto": "^12.0.1", + "@otplib/plugin-thirty-two": "^12.0.1" + } + }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.9.0.tgz", @@ -8345,6 +8393,17 @@ "node": ">=0.10.0" } }, + "node_modules/otplib": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz", + "integrity": "sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==", + "dev": true, + "dependencies": { + "@otplib/core": "^12.0.1", + "@otplib/preset-default": "^12.0.1", + "@otplib/preset-v11": "^12.0.1" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -9167,6 +9226,15 @@ "node": "*" } }, + "node_modules/thirty-two": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", + "integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==", + "dev": true, + "engines": { + "node": ">=0.2.6" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -11398,6 +11466,53 @@ "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", "integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==" }, + "@otplib/core": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/core/-/core-12.0.1.tgz", + "integrity": "sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==", + "dev": true + }, + "@otplib/plugin-crypto": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz", + "integrity": "sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==", + "dev": true, + "requires": { + "@otplib/core": "^12.0.1" + } + }, + "@otplib/plugin-thirty-two": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz", + "integrity": "sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==", + "dev": true, + "requires": { + "@otplib/core": "^12.0.1", + "thirty-two": "^1.0.2" + } + }, + "@otplib/preset-default": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/preset-default/-/preset-default-12.0.1.tgz", + "integrity": "sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==", + "dev": true, + "requires": { + "@otplib/core": "^12.0.1", + "@otplib/plugin-crypto": "^12.0.1", + "@otplib/plugin-thirty-two": "^12.0.1" + } + }, + "@otplib/preset-v11": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@otplib/preset-v11/-/preset-v11-12.0.1.tgz", + "integrity": "sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==", + "dev": true, + "requires": { + "@otplib/core": "^12.0.1", + "@otplib/plugin-crypto": "^12.0.1", + "@otplib/plugin-thirty-two": "^12.0.1" + } + }, "@selderee/plugin-htmlparser2": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.9.0.tgz", @@ -16263,6 +16378,17 @@ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true }, + "otplib": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/otplib/-/otplib-12.0.1.tgz", + "integrity": "sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==", + "dev": true, + "requires": { + "@otplib/core": "^12.0.1", + "@otplib/preset-default": "^12.0.1", + "@otplib/preset-v11": "^12.0.1" + } + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -16885,6 +17011,12 @@ } } }, + "thirty-two": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", + "integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==", + "dev": true + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/packages/core/package.json b/packages/core/package.json index 241d2bdd1..3db1cedab 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,7 +27,8 @@ "jest": "^28.1.3", "jest-fetch-mock": "^3.0.3", "jsdom": "^20.0.0", - "mockdate": "^3.0.5" + "mockdate": "^3.0.5", + "otplib": "^12.0.1" }, "scripts": { "test:e2e": "env-cmd -e e2e jest --forceExit",