From 1b8aab575d36e39083f1bb542d4d11eccf341a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 10 Apr 2024 11:38:38 +0000 Subject: [PATCH 1/2] Add MFA support to CLI --- example.php | 2 +- templates/cli/lib/commands/generic.js.twig | 43 ++++++++++++++-- templates/cli/lib/questions.js.twig | 60 +++++++++++++++++++++- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/example.php b/example.php index bc13d64bc..9e7939cea 100644 --- a/example.php +++ b/example.php @@ -41,7 +41,7 @@ function getSSLPage($url) { $platform = 'console'; // $platform = 'server'; - $spec = getSSLPage("https://raw.githubusercontent.com/appwrite/appwrite/feat-rc-sdks/app/config/specs/swagger2-latest-{$platform}.json"); + $spec = getSSLPage("https://raw.githubusercontent.com/appwrite/appwrite/1.5.x/app/config/specs/swagger2-latest-{$platform}.json"); if(empty($spec)) { throw new Exception('Failed to fetch spec from Appwrite server'); diff --git a/templates/cli/lib/commands/generic.js.twig b/templates/cli/lib/commands/generic.js.twig index c3fe7da4b..f66a36ea3 100644 --- a/templates/cli/lib/commands/generic.js.twig +++ b/templates/cli/lib/commands/generic.js.twig @@ -5,8 +5,8 @@ const { sdkForConsole } = require("../sdks"); const { globalConfig, localConfig } = require("../config"); const { actionRunner, success, parseBool, commandDescriptions, log, parse } = require("../parser"); {% if sdk.test != "true" %} -const { questionsLogin } = require("../questions"); -const { accountCreateEmailPasswordSession, accountDeleteSession } = require("./account"); +const { questionsLogin, questionsListFactors, questionsMfaChallenge } = require("../questions"); +const { accountUpdateMfaChallenge, accountCreateMfaChallenge, accountGet, accountCreateEmailPasswordSession, accountDeleteSession } = require("./account"); const login = new Command("login") .description(commandDescriptions['login']) @@ -25,7 +25,44 @@ const login = new Command("login") sdk: client }) - success() + client.setCookie(globalConfig.getCookie()); + + let account; + + try { + account = await accountGet({ + sdk: client, + parseOutput: false + }); + } catch(error) { + if (error.response === 'user_more_factors_required') { + const { factor } = await inquirer.prompt(questionsListFactors); + + const challenge = await accountCreateMfaChallenge({ + factor, + parseOutput: false, + sdk: client + }); + + const { otp } = await inquirer.prompt(questionsMfaChallenge); + + await accountUpdateMfaChallenge({ + challengeId: challenge.$id, + otp, + parseOutput: false, + sdk: client + }); + + account = await accountGet({ + sdk: client, + parseOutput: false + }); + } else { + throw error; + } + } + + success("Signed in as user with ID: " + account.$id); })); const logout = new Command("logout") diff --git a/templates/cli/lib/questions.js.twig b/templates/cli/lib/questions.js.twig index 0cb4540f3..f75d8daf4 100644 --- a/templates/cli/lib/questions.js.twig +++ b/templates/cli/lib/questions.js.twig @@ -1,6 +1,9 @@ const { localConfig } = require('./config'); const { projectsList } = require('./commands/projects'); const { functionsListRuntimes } = require('./commands/functions'); +const { accountListMfaFactors } = require("./commands/account"); +const { sdkForConsole } = require("./sdks"); + const { databasesList } = require('./commands/databases'); const JSONbig = require("json-bigint")({ storeAsString: false }); @@ -387,7 +390,58 @@ const questionsDeployTeams = [ name: "override", message: 'Are you sure you want to override this team? This can lead to loss of data! Type "YES" to confirm.' }, -] +]; + +const questionsListFactors = [ + { + type: "list", + name: "factor", + message: "Your account is protected by multiple factors. Which factor would you like to use to authenticate?", + choices: async () => { + let client = await sdkForConsole(false); + const factors = await accountListMfaFactors({ + sdk: client, + parseOutput: false + }); + + const choices = [ + { + name: `TOTP (Time-based One-time Password)`, + value: 'totp' + }, + { + name: `E-mail`, + value: 'email' + }, + { + name: `Phone (SMS)`, + value: 'phone' + } + ].filter((ch) => factors[ch.value] === true); + + choices.push({ + name: `Recovery code`, + value: 'recoveryCode' + }); + + return choices; + } + } +]; + +const questionsMfaChallenge = [ + { + type: "input", + name: "otp", + message: "Enter OTP", + validate(value) { + if (!value) { + return "Please enter OTP"; + } + return true; + }, + } +]; module.exports = { questionsInitProject, @@ -398,5 +452,7 @@ module.exports = { questionsDeployCollections, questionsDeployBuckets, questionsDeployTeams, - questionsGetEntrypoint + questionsGetEntrypoint, + questionsListFactors, + questionsMfaChallenge }; From cc6235a2a2d42a0037ffc1f8940cb93dbb7e085b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 10 Apr 2024 15:59:43 +0200 Subject: [PATCH 2/2] Prepare for future fix --- templates/cli/lib/questions.js.twig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/templates/cli/lib/questions.js.twig b/templates/cli/lib/questions.js.twig index f75d8daf4..dd79745ed 100644 --- a/templates/cli/lib/questions.js.twig +++ b/templates/cli/lib/questions.js.twig @@ -416,14 +416,13 @@ const questionsListFactors = [ { name: `Phone (SMS)`, value: 'phone' + }, + { + name: `Recovery code`, + value: 'recoveryCode' } ].filter((ch) => factors[ch.value] === true); - choices.push({ - name: `Recovery code`, - value: 'recoveryCode' - }); - return choices; } }