diff --git a/.gitignore b/.gitignore index d754c0236..2eb932dd0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ dist .DS_STORE nx-cloud.env .idea -.eslintcache \ No newline at end of file +.eslintcache +.env.local \ No newline at end of file diff --git a/apps/mobile/app/components/auth/login.js b/apps/mobile/app/components/auth/login.js index 5ce5f87e1..7c370b67e 100644 --- a/apps/mobile/app/components/auth/login.js +++ b/apps/mobile/app/components/auth/login.js @@ -29,6 +29,7 @@ import PremiumService from "../../services/premium"; import SettingsService from "../../services/settings"; import { useThemeStore } from "../../stores/use-theme-store"; import { useUserStore } from "../../stores/use-user-store"; +import { eCloseSheet } from "../../utils/events"; import { SIZE } from "../../utils/size"; import { sleep } from "../../utils/time"; import BaseDialog from "../dialog/base-dialog"; @@ -119,16 +120,18 @@ export const Login = ({ changeMode }) => { } catch (e) { callback && callback(false); if (e.message === "invalid_grant") { - eSendEvent(eCloseProgressDialog, "two_factor_verify"); + eSendEvent(eCloseSheet, "two_factor_verify"); setLoading(false); } } }, mfaInfo); } + break; } case LoginSteps.passwordAuth: { await db.user.authenticatePassword(email.current, password.current); finishLogin(); + break; } } setLoading(false); @@ -140,7 +143,7 @@ export const Login = ({ changeMode }) => { const finishWithError = async (e) => { setLoading(false); ToastEvent.show({ - heading: user ? "Failed to sync" : "Login failed", + heading: "Login failed", message: e.message, type: "error", context: "local" @@ -325,8 +328,8 @@ export const Login = ({ changeMode }) => { loading ? null : step === LoginSteps.emailAuth - ? "Login to your account" - : "Complete login" + ? "Login" + : "Continue" } /> diff --git a/apps/mobile/app/components/auth/two-factor.js b/apps/mobile/app/components/auth/two-factor.js index 16fc5e3d5..d265b262b 100644 --- a/apps/mobile/app/components/auth/two-factor.js +++ b/apps/mobile/app/components/auth/two-factor.js @@ -204,6 +204,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => { placeholder={ currentMethod.method === "recoveryCode" ? "xxxxxxxx" : "xxxxxx" } + testID={"input.totp"} maxLength={currentMethod.method === "recoveryCode" ? 8 : 6} fwdRef={inputRef} textAlign="center" diff --git a/apps/mobile/e2e/tests/auth.e2e.js b/apps/mobile/e2e/tests/auth.e2e.js index c606a666c..8434c57a5 100644 --- a/apps/mobile/e2e/tests/auth.e2e.js +++ b/apps/mobile/e2e/tests/auth.e2e.js @@ -17,59 +17,64 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +import { authenticator } from "otplib"; import { - tapByText, - prepare, - openSideMenu, elementById, - visibleByText, - sleep + openSideMenu, + prepare, + sleep, + tapByText, + visibleByText } from "./utils"; -const credentials = { - username: "testaccount1@notesnook.com", - password: "testaccount@123" +import dotenv from "dotenv"; +import path from "path"; + +dotenv.config({ path: path.join(__dirname, ".env.local") }); + +const USER = { + login: { + email: process.env.USER_EMAIL, + password: process.env.CURRENT_USER_PASSWORD, + key: process.env.CURRENT_USER_KEY, + totpSecret: process.env.USER_TOTP_SECRET + } }; async function login() { await tapByText("Login to sync your notes."); - await elementById("input.email").typeText(credentials.username); - await elementById("input.password").typeText(credentials.password); + await elementById("input.email").typeText(USER.login.email); + await tapByText("Login"); + await sleep(3000); + await elementById("input.totp").typeText( + authenticator.generate(USER.login.totpSecret) + ); + await sleep(3000); + await elementById("input.password").typeText(USER.login.password); await elementById("input.password").tapReturnKey(); } -async function deleteAccount() { - await tapByText("Account Settings"); - await sleep(2000); - await tapByText("Delete account"); - await elementById("input-value").typeText(credentials.password); - await tapByText("Delete"); - await sleep(5000); -} +// async function deleteAccount() { +// await tapByText("Account Settings"); +// await sleep(2000); +// await tapByText("Delete account"); +// await elementById("input-value").typeText(USER.password); +// await tapByText("Delete"); +// await sleep(5000); +// } -async function signup() { - await tapByText("Login to sync your notes."); - await sleep(500); - await tapByText("Don't have an account? Sign up"); - await elementById("input.email").typeText(credentials.username); - await elementById("input.password").typeText(credentials.password); - await elementById("input.confirmPassword").typeText(credentials.password); - await elementById("input.confirmPassword").tapReturnKey(); -} +// async function signup() { +// await tapByText("Login to sync your notes."); +// await sleep(500); +// await tapByText("Don't have an account? Sign up"); +// await elementById("input.email").typeText(USER.signup.email); +// await elementById("input.password").typeText(USER.signup.password); +// await elementById("input.confirmPassword").typeText(USER.signup.password); +// await elementById("input.confirmPassword").tapReturnKey(); +// } describe("AUTH", () => { - it("Sign up", async () => { - await prepare(); - await openSideMenu(); - await signup(); - await sleep(5000); - await device.pressBack(); - await sleep(5000); - await openSideMenu(); - await visibleByText("Tap here to sync your notes."); - }); - - it("Login to account", async () => { + it("Login", async () => { await prepare(); await openSideMenu(); await login(); @@ -77,15 +82,4 @@ describe("AUTH", () => { await openSideMenu(); await visibleByText("Tap here to sync your notes."); }); - - it("Delete account", async () => { - await prepare(); - await openSideMenu(); - await login(); - await sleep(15000); - await openSideMenu(); - await tapByText("Settings"); - await sleep(1000); - await deleteAccount(); - }); }); diff --git a/apps/mobile/package-lock.json b/apps/mobile/package-lock.json index f08ab8b4b..20dd9fcaa 100644 --- a/apps/mobile/package-lock.json +++ b/apps/mobile/package-lock.json @@ -19,6 +19,7 @@ "react-native-iap": "7.5.6" }, "devDependencies": { + "otplib": "12.0.1", "patch-package": "^6.4.7", "typescript": "^4.8.2" } @@ -4002,6 +4003,53 @@ "react-native": "*" } }, + "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/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.8.tgz", @@ -16822,6 +16870,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-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -20424,6 +20483,15 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "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/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", @@ -24334,6 +24402,53 @@ "resolved": "https://registry.npmjs.org/@notifee/react-native/-/react-native-7.1.0.tgz", "integrity": "sha512-FmYplZ1Bxua2q8u+W3RmVx85R32DWeMQGfzvu8O9w7bZQwMtvmQROjfyMSIOMVCQw6fSA7zmMaikC+W4LjLMHQ==" }, + "@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" + } + }, "@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.8.tgz", @@ -33907,6 +34022,17 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" }, + "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-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -36592,6 +36718,12 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "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 + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 028348792..55f7888bd 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -24,7 +24,8 @@ }, "devDependencies": { "patch-package": "^6.4.7", - "typescript": "^4.8.2" + "typescript": "^4.8.2", + "otplib": "12.0.1" }, "dependencies": { "@notesnook/core": "*",