Skip to content

Commit b4c6327

Browse files
Merge pull request #807 from appwrite/feat-cli-mfa
Feat: MFA support for CLI
2 parents 6b7dc91 + cc6235a commit b4c6327

File tree

3 files changed

+98
-6
lines changed

3 files changed

+98
-6
lines changed

example.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function getSSLPage($url) {
4141
$platform = 'console';
4242
// $platform = 'server';
4343

44-
$spec = getSSLPage("https://raw.githubusercontent.com/appwrite/appwrite/feat-rc-sdks/app/config/specs/swagger2-latest-{$platform}.json");
44+
$spec = getSSLPage("https://raw.githubusercontent.com/appwrite/appwrite/1.5.x/app/config/specs/swagger2-latest-{$platform}.json");
4545

4646
if(empty($spec)) {
4747
throw new Exception('Failed to fetch spec from Appwrite server');

templates/cli/lib/commands/generic.js.twig

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ const { sdkForConsole } = require("../sdks");
55
const { globalConfig, localConfig } = require("../config");
66
const { actionRunner, success, parseBool, commandDescriptions, log, parse } = require("../parser");
77
{% if sdk.test != "true" %}
8-
const { questionsLogin } = require("../questions");
9-
const { accountCreateEmailPasswordSession, accountDeleteSession } = require("./account");
8+
const { questionsLogin, questionsListFactors, questionsMfaChallenge } = require("../questions");
9+
const { accountUpdateMfaChallenge, accountCreateMfaChallenge, accountGet, accountCreateEmailPasswordSession, accountDeleteSession } = require("./account");
1010

1111
const login = new Command("login")
1212
.description(commandDescriptions['login'])
@@ -25,7 +25,44 @@ const login = new Command("login")
2525
sdk: client
2626
})
2727

28-
success()
28+
client.setCookie(globalConfig.getCookie());
29+
30+
let account;
31+
32+
try {
33+
account = await accountGet({
34+
sdk: client,
35+
parseOutput: false
36+
});
37+
} catch(error) {
38+
if (error.response === 'user_more_factors_required') {
39+
const { factor } = await inquirer.prompt(questionsListFactors);
40+
41+
const challenge = await accountCreateMfaChallenge({
42+
factor,
43+
parseOutput: false,
44+
sdk: client
45+
});
46+
47+
const { otp } = await inquirer.prompt(questionsMfaChallenge);
48+
49+
await accountUpdateMfaChallenge({
50+
challengeId: challenge.$id,
51+
otp,
52+
parseOutput: false,
53+
sdk: client
54+
});
55+
56+
account = await accountGet({
57+
sdk: client,
58+
parseOutput: false
59+
});
60+
} else {
61+
throw error;
62+
}
63+
}
64+
65+
success("Signed in as user with ID: " + account.$id);
2966
}));
3067

3168
const logout = new Command("logout")

templates/cli/lib/questions.js.twig

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const { localConfig } = require('./config');
22
const { projectsList } = require('./commands/projects');
33
const { functionsListRuntimes } = require('./commands/functions');
4+
const { accountListMfaFactors } = require("./commands/account");
5+
const { sdkForConsole } = require("./sdks");
6+
47
const { databasesList } = require('./commands/databases');
58
const JSONbig = require("json-bigint")({ storeAsString: false });
69

@@ -387,7 +390,57 @@ const questionsDeployTeams = [
387390
name: "override",
388391
message: 'Are you sure you want to override this team? This can lead to loss of data! Type "YES" to confirm.'
389392
},
390-
]
393+
];
394+
395+
const questionsListFactors = [
396+
{
397+
type: "list",
398+
name: "factor",
399+
message: "Your account is protected by multiple factors. Which factor would you like to use to authenticate?",
400+
choices: async () => {
401+
let client = await sdkForConsole(false);
402+
const factors = await accountListMfaFactors({
403+
sdk: client,
404+
parseOutput: false
405+
});
406+
407+
const choices = [
408+
{
409+
name: `TOTP (Time-based One-time Password)`,
410+
value: 'totp'
411+
},
412+
{
413+
name: `E-mail`,
414+
value: 'email'
415+
},
416+
{
417+
name: `Phone (SMS)`,
418+
value: 'phone'
419+
},
420+
{
421+
name: `Recovery code`,
422+
value: 'recoveryCode'
423+
}
424+
].filter((ch) => factors[ch.value] === true);
425+
426+
return choices;
427+
}
428+
}
429+
];
430+
431+
const questionsMfaChallenge = [
432+
{
433+
type: "input",
434+
name: "otp",
435+
message: "Enter OTP",
436+
validate(value) {
437+
if (!value) {
438+
return "Please enter OTP";
439+
}
440+
return true;
441+
},
442+
}
443+
];
391444

392445
module.exports = {
393446
questionsInitProject,
@@ -398,5 +451,7 @@ module.exports = {
398451
questionsDeployCollections,
399452
questionsDeployBuckets,
400453
questionsDeployTeams,
401-
questionsGetEntrypoint
454+
questionsGetEntrypoint,
455+
questionsListFactors,
456+
questionsMfaChallenge
402457
};

0 commit comments

Comments
 (0)