1
0
mirror of https://github.com/bitwarden/jslib synced 2025-12-19 09:43:28 +00:00

Update clients to use new authService interface

This commit is contained in:
Thomas Rittson
2021-12-20 08:01:31 +10:00
parent 0e10ee2c50
commit bb04c5bf86
6 changed files with 159 additions and 164 deletions

View File

@@ -93,7 +93,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
} }
try { try {
this.formPromise = this.authService.logIn(this.email, this.masterPassword, this.captchaToken); this.formPromise = this.authService.logIn(this.email, this.masterPassword, null, this.captchaToken);
const response = await this.formPromise; const response = await this.formPromise;
if (this.rememberEmail) { if (this.rememberEmail) {
await this.stateService.setRememberedEmail(this.email); await this.stateService.setRememberedEmail(this.email);

View File

@@ -193,9 +193,11 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
async doSubmit() { async doSubmit() {
this.formPromise = this.authService.logInTwoFactor( this.formPromise = this.authService.logInTwoFactor(
this.selectedProviderType, {
this.token, provider: this.selectedProviderType,
this.remember token: this.token,
remember: this.remember
}
); );
const response: AuthResult = await this.formPromise; const response: AuthResult = await this.formPromise;
const disableFavicon = await this.stateService.getDisableFavicon(); const disableFavicon = await this.stateService.getDisableFavicon();

View File

@@ -11,15 +11,15 @@ export abstract class AuthService {
clientId: string; clientId: string;
clientSecret: string; clientSecret: string;
logIn: (email: string, masterPassword: string, twoFactor: TwoFactorData, captchaToken?: string) => Promise<AuthResult>; logIn: (email: string, masterPassword: string, twoFactor?: TwoFactorData, captchaToken?: string) => Promise<AuthResult>;
logInSso: ( logInSso: (
code: string, code: string,
codeVerifier: string, codeVerifier: string,
redirectUrl: string, redirectUrl: string,
twoFactor: TwoFactorData, orgId: string,
orgId: string twoFactor?: TwoFactorData,
) => Promise<AuthResult>; ) => Promise<AuthResult>;
logInApiKey: (clientId: string, clientSecret: string, twoFactor: TwoFactorData) => Promise<AuthResult>; logInApiKey: (clientId: string, clientSecret: string, twoFactor?: TwoFactorData) => Promise<AuthResult>;
logInTwoFactor: ( logInTwoFactor: (
twoFactor: TwoFactorData twoFactor: TwoFactorData
) => Promise<AuthResult>; ) => Promise<AuthResult>;

View File

@@ -65,7 +65,7 @@ export class AuthService implements AuthServiceAbstraction {
private setCryptoKeys = true private setCryptoKeys = true
) {} ) {}
async logIn(email: string, masterPassword: string, twoFactor: TwoFactorData, captchaToken?: string): Promise<AuthResult> { async logIn(email: string, masterPassword: string, twoFactor?: TwoFactorData, captchaToken?: string): Promise<AuthResult> {
this.twoFactorService.clearSelectedProvider(); this.twoFactorService.clearSelectedProvider();
const key = await this.makePreloginKey(masterPassword, email); const key = await this.makePreloginKey(masterPassword, email);
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key); const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
@@ -94,8 +94,8 @@ export class AuthService implements AuthServiceAbstraction {
code: string, code: string,
codeVerifier: string, codeVerifier: string,
redirectUrl: string, redirectUrl: string,
twoFactor: TwoFactorData, orgId: string,
orgId: string twoFactor?: TwoFactorData,
): Promise<AuthResult> { ): Promise<AuthResult> {
this.twoFactorService.clearSelectedProvider(); this.twoFactorService.clearSelectedProvider();
return await this.logInHelper( return await this.logInHelper(
@@ -114,7 +114,7 @@ export class AuthService implements AuthServiceAbstraction {
); );
} }
async logInApiKey(clientId: string, clientSecret: string, twoFactor: TwoFactorData): Promise<AuthResult> { async logInApiKey(clientId: string, clientSecret: string, twoFactor?: TwoFactorData): Promise<AuthResult> {
this.twoFactorService.clearSelectedProvider(); this.twoFactorService.clearSelectedProvider();
return await this.logInHelper( return await this.logInHelper(
null, null,

View File

@@ -156,155 +156,148 @@ export class LoginCommand {
} }
let response: AuthResult = null; let response: AuthResult = null;
if (twoFactorToken != null && twoFactorMethod != null) { if (clientId != null && clientSecret != null) {
if (clientId != null && clientSecret != null) { response = await this.authService.logInApiKey(
response = await this.authService.logInApiKeyComplete( clientId,
clientId, clientSecret,
clientSecret, {
twoFactorMethod, provider: twoFactorMethod,
twoFactorToken, token: twoFactorToken,
false remember: false
); }
} else if (ssoCode != null && ssoCodeVerifier != null) { );
response = await this.authService.logInSsoComplete( } else if (ssoCode != null && ssoCodeVerifier != null) {
ssoCode, response = await this.authService.logInSso(
ssoCodeVerifier, ssoCode,
this.ssoRedirectUri, ssoCodeVerifier,
twoFactorMethod, this.ssoRedirectUri,
twoFactorToken, orgIdentifier,
false {
); provider: twoFactorMethod,
} else { token: twoFactorToken,
response = await this.authService.logInComplete( remember: false
}
);
} else {
response = await this.authService.logIn(
email,
password,
{
provider: twoFactorMethod,
token: twoFactorToken,
remember: false,
}
);
}
if (response.captchaSiteKey) {
const badCaptcha = Response.badRequest(
"Your authentication request appears to be coming from a bot\n" +
"Please use your API key to validate this request and ensure BW_CLIENTSECRET is correct, if set.\n" +
"(https://bitwarden.com/help/article/cli-auth-challenges)"
);
try {
const captchaClientSecret = await this.apiClientSecret(true);
if (Utils.isNullOrWhitespace(captchaClientSecret)) {
return badCaptcha;
}
const secondResponse = await this.authService.logIn(
email, email,
password, password,
twoFactorMethod, {
twoFactorToken, provider: twoFactorMethod,
false, token: twoFactorToken,
this.clientSecret remember: false,
},
captchaClientSecret
); );
} response = secondResponse;
} else { } catch (e) {
if (clientId != null && clientSecret != null) {
response = await this.authService.logInApiKey(clientId, clientSecret);
} else if (ssoCode != null && ssoCodeVerifier != null) {
response = await this.authService.logInSso(
ssoCode,
ssoCodeVerifier,
this.ssoRedirectUri,
orgIdentifier
);
} else {
response = await this.authService.logIn(email, password);
}
if (response.captchaSiteKey) {
const badCaptcha = Response.badRequest(
"Your authentication request appears to be coming from a bot\n" +
"Please use your API key to validate this request and ensure BW_CLIENTSECRET is correct, if set.\n" +
"(https://bitwarden.com/help/article/cli-auth-challenges)"
);
try {
const captchaClientSecret = await this.apiClientSecret(true);
if (Utils.isNullOrWhitespace(captchaClientSecret)) {
return badCaptcha;
}
const secondResponse = await this.authService.logInComplete(
email,
password,
twoFactorMethod,
twoFactorToken,
false,
captchaClientSecret
);
response = secondResponse;
} catch (e) {
if (
(e instanceof ErrorResponse || e.constructor.name === "ErrorResponse") &&
(e as ErrorResponse).message.includes("Captcha is invalid")
) {
return badCaptcha;
} else {
throw e;
}
}
}
if (response.twoFactor) {
let selectedProvider: any = null;
const twoFactorProviders = this.twoFactorService.getSupportedProviders(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 if (this.canInteract) {
const twoFactorOptions = twoFactorProviders.map((p) => p.name);
twoFactorOptions.push(new inquirer.Separator());
twoFactorOptions.push("Cancel");
const answer: inquirer.Answers = await inquirer.createPromptModule({
output: process.stderr,
})({
type: "list",
name: "method",
message: "Two-step login method:",
choices: twoFactorOptions,
});
const i = twoFactorOptions.indexOf(answer.method);
if (i === twoFactorOptions.length - 1) {
return Response.error("Login failed.");
}
selectedProvider = twoFactorProviders[i];
}
if (selectedProvider == null) {
return Response.error("Login failed. No provider selected.");
}
}
if ( if (
twoFactorToken == null && (e instanceof ErrorResponse || e.constructor.name === "ErrorResponse") &&
response.twoFactorProviders.size > 1 && (e as ErrorResponse).message.includes("Captcha is invalid")
selectedProvider.type === TwoFactorProviderType.Email
) { ) {
const emailReq = new TwoFactorEmailRequest(); return badCaptcha;
emailReq.email = this.authService.email; } else {
emailReq.masterPasswordHash = this.authService.masterPasswordHash; throw e;
await this.apiService.postTwoFactorEmail(emailReq);
} }
if (twoFactorToken == null) {
if (this.canInteract) {
const answer: inquirer.Answers = await inquirer.createPromptModule({
output: process.stderr,
})({
type: "input",
name: "token",
message: "Two-step login code:",
});
twoFactorToken = answer.token;
}
if (twoFactorToken == null || twoFactorToken === "") {
return Response.badRequest("Code is required.");
}
}
response = await this.authService.logInTwoFactor(
selectedProvider.type,
twoFactorToken,
false
);
} }
} }
if (response.twoFactor) {
let selectedProvider: any = null;
const twoFactorProviders = this.twoFactorService.getSupportedProviders(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 if (this.canInteract) {
const twoFactorOptions = twoFactorProviders.map((p) => p.name);
twoFactorOptions.push(new inquirer.Separator());
twoFactorOptions.push("Cancel");
const answer: inquirer.Answers = await inquirer.createPromptModule({
output: process.stderr,
})({
type: "list",
name: "method",
message: "Two-step login method:",
choices: twoFactorOptions,
});
const i = twoFactorOptions.indexOf(answer.method);
if (i === twoFactorOptions.length - 1) {
return Response.error("Login failed.");
}
selectedProvider = twoFactorProviders[i];
}
if (selectedProvider == null) {
return Response.error("Login failed. No provider selected.");
}
}
if (
twoFactorToken == null &&
response.twoFactorProviders.size > 1 &&
selectedProvider.type === TwoFactorProviderType.Email
) {
const emailReq = new TwoFactorEmailRequest();
emailReq.email = this.authService.email;
emailReq.masterPasswordHash = this.authService.masterPasswordHash;
await this.apiService.postTwoFactorEmail(emailReq);
}
if (twoFactorToken == null) {
if (this.canInteract) {
const answer: inquirer.Answers = await inquirer.createPromptModule({
output: process.stderr,
})({
type: "input",
name: "token",
message: "Two-step login code:",
});
twoFactorToken = answer.token;
}
if (twoFactorToken == null || twoFactorToken === "") {
return Response.badRequest("Code is required.");
}
}
response = await this.authService.logInTwoFactor({
provider: selectedProvider.type,
token: twoFactorToken,
remember: false,
});
}
if (response.twoFactor) { if (response.twoFactor) {
return Response.error("Login failed."); return Response.error("Login failed.");

View File

@@ -190,7 +190,7 @@ describe("Cipher Service", () => {
const expected = newAuthResponse(); const expected = newAuthResponse();
// Act // Act
const result = await authService.logIn(email, masterPassword, null); const result = await authService.logIn(email, masterPassword);
// Assert // Assert
// Api call: // Api call:
@@ -240,7 +240,7 @@ describe("Cipher Service", () => {
expected.captchaSiteKey = siteKey; expected.captchaSiteKey = siteKey;
// Act // Act
const result = await authService.logIn(email, masterPassword, null); const result = await authService.logIn(email, masterPassword);
// Assertions // Assertions
stateService.didNotReceive().addAccount(Arg.any()); stateService.didNotReceive().addAccount(Arg.any());
@@ -274,7 +274,7 @@ describe("Cipher Service", () => {
); );
// Act // Act
const result = await authService.logIn(email, masterPassword, null); const result = await authService.logIn(email, masterPassword);
// Assertions // Assertions
commonSuccessAssertions(); commonSuccessAssertions();
@@ -293,7 +293,7 @@ describe("Cipher Service", () => {
tokenService.getTwoFactorToken(email).resolves(null); tokenService.getTwoFactorToken(email).resolves(null);
apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); apiService.postIdentityToken(Arg.any()).resolves(tokenResponse);
const result = await authService.logIn(email, masterPassword, null); const result = await authService.logIn(email, masterPassword);
commonSuccessAssertions(); commonSuccessAssertions();
apiService.received(1).postAccountKeys(Arg.any()); apiService.received(1).postAccountKeys(Arg.any());
@@ -317,7 +317,7 @@ describe("Cipher Service", () => {
expected.twoFactorProviders = twoFactorProviders; expected.twoFactorProviders = twoFactorProviders;
expected.captchaSiteKey = undefined; expected.captchaSiteKey = undefined;
const result = await authService.logIn(email, masterPassword, null); const result = await authService.logIn(email, masterPassword);
stateService.didNotReceive().addAccount(Arg.any()); stateService.didNotReceive().addAccount(Arg.any());
messagingService.didNotReceive().send(Arg.any()); messagingService.didNotReceive().send(Arg.any());
@@ -331,7 +331,7 @@ describe("Cipher Service", () => {
tokenService.getTwoFactorToken(email).resolves(twoFactorToken); tokenService.getTwoFactorToken(email).resolves(twoFactorToken);
await authService.logIn(email, masterPassword, null); await authService.logIn(email, masterPassword);
apiService.received(1).postIdentityToken( apiService.received(1).postIdentityToken(
Arg.is((actual) => { Arg.is((actual) => {
@@ -406,7 +406,7 @@ describe("Cipher Service", () => {
tokenService.getTwoFactorToken(null).resolves(null); tokenService.getTwoFactorToken(null).resolves(null);
apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); apiService.postIdentityToken(Arg.any()).resolves(tokenResponse);
const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, null, ssoOrgId); const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId);
// Assert // Assert
// Api call: // Api call:
@@ -452,7 +452,7 @@ describe("Cipher Service", () => {
tokenService.getTwoFactorToken(null).resolves(null); tokenService.getTwoFactorToken(null).resolves(null);
apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); apiService.postIdentityToken(Arg.any()).resolves(tokenResponse);
const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, null, ssoOrgId); const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId);
// Assert // Assert
cryptoService.didNotReceive().setEncPrivateKey(privateKey); cryptoService.didNotReceive().setEncPrivateKey(privateKey);
@@ -466,7 +466,7 @@ describe("Cipher Service", () => {
apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); apiService.postIdentityToken(Arg.any()).resolves(tokenResponse);
const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, null, ssoOrgId); const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId);
commonSuccessAssertions(); commonSuccessAssertions();
keyConnectorService.received(1).getAndSetKey(keyConnectorUrl); keyConnectorService.received(1).getAndSetKey(keyConnectorUrl);
@@ -500,7 +500,7 @@ describe("Cipher Service", () => {
apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); apiService.postIdentityToken(Arg.any()).resolves(tokenResponse);
const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, null, ssoOrgId); const result = await authService.logInSso(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId);
commonSuccessAssertions(); commonSuccessAssertions();
cryptoService.received(1).setKey(preloginKey); cryptoService.received(1).setKey(preloginKey);
@@ -529,7 +529,7 @@ describe("Cipher Service", () => {
const tokenResponse = newTokenResponse(); const tokenResponse = newTokenResponse();
apiService.postIdentityToken(Arg.any()).resolves(tokenResponse); apiService.postIdentityToken(Arg.any()).resolves(tokenResponse);
const result = await authService.logInApiKey(apiClientId, apiClientSecret, null); const result = await authService.logInApiKey(apiClientId, apiClientSecret);
apiService.received(1).postIdentityToken( apiService.received(1).postIdentityToken(
Arg.is((actual) => { Arg.is((actual) => {