parse two step login options
This commit is contained in:
parent
e3eea736ed
commit
e8a3325ec9
|
@ -1,18 +1,22 @@
|
||||||
import * as program from 'commander';
|
import * as program from 'commander';
|
||||||
import * as readline from 'readline-sync';
|
import * as readline from 'readline-sync';
|
||||||
|
|
||||||
import { AuthResult } from 'jslib/models/domain/authResult';
|
import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
|
||||||
|
|
||||||
|
import { AuthResult } from 'jslib/models/domain/authResult';
|
||||||
|
import { TwoFactorEmailRequest } from 'jslib/models/request/twoFactorEmailRequest';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib/abstractions/api.service';
|
||||||
import { AuthService } from 'jslib/abstractions/auth.service';
|
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||||
|
|
||||||
import { Response } from '../models/response';
|
import { Response } from '../models/response';
|
||||||
|
|
||||||
export class LoginCommand {
|
export class LoginCommand {
|
||||||
constructor(private authService: AuthService) { }
|
constructor(private authService: AuthService, private apiService: ApiService) { }
|
||||||
|
|
||||||
async run(email: string, password: string, cmd: program.Command) {
|
async run(email: string, password: string, cmd: program.Command) {
|
||||||
if (email == null || email === '') {
|
if (email == null || email === '') {
|
||||||
email = readline.question('Email Address: ');
|
email = readline.question('Email address: ');
|
||||||
}
|
}
|
||||||
if (email == null || email.trim() === '') {
|
if (email == null || email.trim() === '') {
|
||||||
return Response.badRequest('Email address is required.');
|
return Response.badRequest('Email address is required.');
|
||||||
|
@ -22,7 +26,7 @@ export class LoginCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password == null || password === '') {
|
if (password == null || password === '') {
|
||||||
password = readline.question('Master Password: ', {
|
password = readline.question('Master password: ', {
|
||||||
hideEchoBack: true,
|
hideEchoBack: true,
|
||||||
mask: '*',
|
mask: '*',
|
||||||
});
|
});
|
||||||
|
@ -31,35 +35,70 @@ export class LoginCommand {
|
||||||
return Response.badRequest('Master password is required.');
|
return Response.badRequest('Master password is required.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let twoFactorToken: string = cmd.code;
|
||||||
|
let twoFactorMethod: TwoFactorProviderType = null;
|
||||||
try {
|
try {
|
||||||
const response = await this.authService.logIn(email, password);
|
if (cmd.method != null) {
|
||||||
if (response.twoFactor) {
|
twoFactorMethod = parseInt(cmd.method, null);
|
||||||
let selectedProvider: any = null;
|
}
|
||||||
const twoFactorProviders = this.authService.getSupportedTwoFactorProviders(null);
|
} catch (e) {
|
||||||
if (twoFactorProviders.length === 0) {
|
return Response.error('Invalid two-step login method.');
|
||||||
return Response.badRequest('No providers available for this client.');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (twoFactorProviders.length === 1) {
|
try {
|
||||||
selectedProvider = twoFactorProviders[0];
|
let response: AuthResult = null;
|
||||||
} else {
|
if (twoFactorToken != null && twoFactorMethod != null) {
|
||||||
const options = twoFactorProviders.map((p) => p.name);
|
response = await this.authService.logInComplete(email, password, twoFactorMethod,
|
||||||
const i = readline.keyInSelect(options, 'Two-step login method: ', { cancel: 'Cancel' });
|
twoFactorToken, false);
|
||||||
if (i < 0) {
|
} else {
|
||||||
|
response = await this.authService.logIn(email, password);
|
||||||
|
if (response.twoFactor) {
|
||||||
|
let selectedProvider: any = null;
|
||||||
|
const twoFactorProviders = this.authService.getSupportedTwoFactorProviders(null);
|
||||||
|
if (twoFactorProviders.length === 0) {
|
||||||
|
return Response.badRequest('No providers available for this client.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twoFactorMethod != null) {
|
||||||
|
try {
|
||||||
|
selectedProvider = twoFactorProviders.filter((p) => p.type === twoFactorMethod)[0];
|
||||||
|
} catch (e) {
|
||||||
|
return Response.error('Invalid two-step login method.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedProvider == null) {
|
||||||
|
if (twoFactorProviders.length === 1) {
|
||||||
|
selectedProvider = twoFactorProviders[0];
|
||||||
|
} else {
|
||||||
|
const options = twoFactorProviders.map((p) => p.name);
|
||||||
|
const i = readline.keyInSelect(options, 'Two-step login method: ', { cancel: 'Cancel' });
|
||||||
|
if (i < 0) {
|
||||||
|
return Response.error('Login failed.');
|
||||||
|
}
|
||||||
|
selectedProvider = twoFactorProviders[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twoFactorToken == null && response.twoFactorProviders.size > 1 &&
|
||||||
|
selectedProvider.type === TwoFactorProviderType.Email) {
|
||||||
|
const emailReq = new TwoFactorEmailRequest(this.authService.email,
|
||||||
|
this.authService.masterPasswordHash);
|
||||||
|
await this.apiService.postTwoFactorEmail(emailReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (twoFactorToken == null) {
|
||||||
|
twoFactorToken = readline.question('Two-step login code for ' + selectedProvider.name + ': ');
|
||||||
|
if (twoFactorToken == null || twoFactorToken === '') {
|
||||||
|
return Response.badRequest('Code is required.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const twoFactorResponse = await this.authService.logInTwoFactor(selectedProvider.type,
|
||||||
|
twoFactorToken, false);
|
||||||
|
if (twoFactorResponse.twoFactor) {
|
||||||
return Response.error('Login failed.');
|
return Response.error('Login failed.');
|
||||||
}
|
}
|
||||||
selectedProvider = twoFactorProviders[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
const twoFactorToken = readline.question('Two-step login token for ' + selectedProvider.name + ': ');
|
|
||||||
if (twoFactorToken == null || twoFactorToken === '') {
|
|
||||||
return Response.badRequest('Token is required.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const twoFactorResponse = await this.authService.logInTwoFactor(selectedProvider.type,
|
|
||||||
twoFactorToken, false);
|
|
||||||
if (twoFactorResponse.twoFactor) {
|
|
||||||
return Response.error('Login failed.');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Response.success();
|
return Response.success();
|
||||||
|
|
|
@ -29,12 +29,12 @@ export class Program {
|
||||||
.option('--pretty', 'Format stdout.');
|
.option('--pretty', 'Format stdout.');
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('login <email> <password>')
|
.command('login [email] [password]')
|
||||||
.description('Log into a Bitwarden user account.')
|
.description('Log into a Bitwarden user account.')
|
||||||
.option('-m, --method <method>', '2FA method.')
|
.option('-m, --method <method>', 'Two-step login method.')
|
||||||
.option('-c, --code <code>', '2FA code.')
|
.option('-c, --code <code>', 'Two-step login code.')
|
||||||
.action(async (email: string, password: string, cmd: program.Command) => {
|
.action(async (email: string, password: string, cmd: program.Command) => {
|
||||||
const command = new LoginCommand(this.main.authService);
|
const command = new LoginCommand(this.main.authService, this.main.apiService);
|
||||||
const response = await command.run(email, password, cmd);
|
const response = await command.run(email, password, cmd);
|
||||||
this.processResponse(response, cmd);
|
this.processResponse(response, cmd);
|
||||||
});
|
});
|
||||||
|
@ -54,7 +54,7 @@ export class Program {
|
||||||
});
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('unlock <password>')
|
.command('unlock [password]')
|
||||||
.description('Unlock the vault and obtain a new session token.')
|
.description('Unlock the vault and obtain a new session token.')
|
||||||
.action((cmd) => {
|
.action((cmd) => {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
Loading…
Reference in New Issue