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:
@@ -93,7 +93,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit
|
||||
}
|
||||
|
||||
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;
|
||||
if (this.rememberEmail) {
|
||||
await this.stateService.setRememberedEmail(this.email);
|
||||
|
||||
@@ -193,9 +193,11 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
|
||||
|
||||
async doSubmit() {
|
||||
this.formPromise = this.authService.logInTwoFactor(
|
||||
this.selectedProviderType,
|
||||
this.token,
|
||||
this.remember
|
||||
{
|
||||
provider: this.selectedProviderType,
|
||||
token: this.token,
|
||||
remember: this.remember
|
||||
}
|
||||
);
|
||||
const response: AuthResult = await this.formPromise;
|
||||
const disableFavicon = await this.stateService.getDisableFavicon();
|
||||
|
||||
@@ -11,15 +11,15 @@ export abstract class AuthService {
|
||||
clientId: 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: (
|
||||
code: string,
|
||||
codeVerifier: string,
|
||||
redirectUrl: string,
|
||||
twoFactor: TwoFactorData,
|
||||
orgId: string
|
||||
orgId: string,
|
||||
twoFactor?: TwoFactorData,
|
||||
) => Promise<AuthResult>;
|
||||
logInApiKey: (clientId: string, clientSecret: string, twoFactor: TwoFactorData) => Promise<AuthResult>;
|
||||
logInApiKey: (clientId: string, clientSecret: string, twoFactor?: TwoFactorData) => Promise<AuthResult>;
|
||||
logInTwoFactor: (
|
||||
twoFactor: TwoFactorData
|
||||
) => Promise<AuthResult>;
|
||||
|
||||
@@ -65,7 +65,7 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
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();
|
||||
const key = await this.makePreloginKey(masterPassword, email);
|
||||
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
|
||||
@@ -94,8 +94,8 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
code: string,
|
||||
codeVerifier: string,
|
||||
redirectUrl: string,
|
||||
twoFactor: TwoFactorData,
|
||||
orgId: string
|
||||
orgId: string,
|
||||
twoFactor?: TwoFactorData,
|
||||
): Promise<AuthResult> {
|
||||
this.twoFactorService.clearSelectedProvider();
|
||||
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();
|
||||
return await this.logInHelper(
|
||||
null,
|
||||
|
||||
@@ -156,155 +156,148 @@ export class LoginCommand {
|
||||
}
|
||||
|
||||
let response: AuthResult = null;
|
||||
if (twoFactorToken != null && twoFactorMethod != null) {
|
||||
if (clientId != null && clientSecret != null) {
|
||||
response = await this.authService.logInApiKeyComplete(
|
||||
clientId,
|
||||
clientSecret,
|
||||
twoFactorMethod,
|
||||
twoFactorToken,
|
||||
false
|
||||
);
|
||||
} else if (ssoCode != null && ssoCodeVerifier != null) {
|
||||
response = await this.authService.logInSsoComplete(
|
||||
ssoCode,
|
||||
ssoCodeVerifier,
|
||||
this.ssoRedirectUri,
|
||||
twoFactorMethod,
|
||||
twoFactorToken,
|
||||
false
|
||||
);
|
||||
} else {
|
||||
response = await this.authService.logInComplete(
|
||||
if (clientId != null && clientSecret != null) {
|
||||
response = await this.authService.logInApiKey(
|
||||
clientId,
|
||||
clientSecret,
|
||||
{
|
||||
provider: twoFactorMethod,
|
||||
token: twoFactorToken,
|
||||
remember: false
|
||||
}
|
||||
);
|
||||
} else if (ssoCode != null && ssoCodeVerifier != null) {
|
||||
response = await this.authService.logInSso(
|
||||
ssoCode,
|
||||
ssoCodeVerifier,
|
||||
this.ssoRedirectUri,
|
||||
orgIdentifier,
|
||||
{
|
||||
provider: twoFactorMethod,
|
||||
token: twoFactorToken,
|
||||
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,
|
||||
password,
|
||||
twoFactorMethod,
|
||||
twoFactorToken,
|
||||
false,
|
||||
this.clientSecret
|
||||
{
|
||||
provider: twoFactorMethod,
|
||||
token: twoFactorToken,
|
||||
remember: false,
|
||||
},
|
||||
captchaClientSecret
|
||||
);
|
||||
}
|
||||
} else {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
response = secondResponse;
|
||||
} catch (e) {
|
||||
if (
|
||||
twoFactorToken == null &&
|
||||
response.twoFactorProviders.size > 1 &&
|
||||
selectedProvider.type === TwoFactorProviderType.Email
|
||||
(e instanceof ErrorResponse || e.constructor.name === "ErrorResponse") &&
|
||||
(e as ErrorResponse).message.includes("Captcha is invalid")
|
||||
) {
|
||||
const emailReq = new TwoFactorEmailRequest();
|
||||
emailReq.email = this.authService.email;
|
||||
emailReq.masterPasswordHash = this.authService.masterPasswordHash;
|
||||
await this.apiService.postTwoFactorEmail(emailReq);
|
||||
return badCaptcha;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
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) {
|
||||
return Response.error("Login failed.");
|
||||
|
||||
@@ -190,7 +190,7 @@ describe("Cipher Service", () => {
|
||||
const expected = newAuthResponse();
|
||||
|
||||
// Act
|
||||
const result = await authService.logIn(email, masterPassword, null);
|
||||
const result = await authService.logIn(email, masterPassword);
|
||||
|
||||
// Assert
|
||||
// Api call:
|
||||
@@ -240,7 +240,7 @@ describe("Cipher Service", () => {
|
||||
expected.captchaSiteKey = siteKey;
|
||||
|
||||
// Act
|
||||
const result = await authService.logIn(email, masterPassword, null);
|
||||
const result = await authService.logIn(email, masterPassword);
|
||||
|
||||
// Assertions
|
||||
stateService.didNotReceive().addAccount(Arg.any());
|
||||
@@ -274,7 +274,7 @@ describe("Cipher Service", () => {
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await authService.logIn(email, masterPassword, null);
|
||||
const result = await authService.logIn(email, masterPassword);
|
||||
|
||||
// Assertions
|
||||
commonSuccessAssertions();
|
||||
@@ -293,7 +293,7 @@ describe("Cipher Service", () => {
|
||||
tokenService.getTwoFactorToken(email).resolves(null);
|
||||
apiService.postIdentityToken(Arg.any()).resolves(tokenResponse);
|
||||
|
||||
const result = await authService.logIn(email, masterPassword, null);
|
||||
const result = await authService.logIn(email, masterPassword);
|
||||
|
||||
commonSuccessAssertions();
|
||||
apiService.received(1).postAccountKeys(Arg.any());
|
||||
@@ -317,7 +317,7 @@ describe("Cipher Service", () => {
|
||||
expected.twoFactorProviders = twoFactorProviders;
|
||||
expected.captchaSiteKey = undefined;
|
||||
|
||||
const result = await authService.logIn(email, masterPassword, null);
|
||||
const result = await authService.logIn(email, masterPassword);
|
||||
|
||||
stateService.didNotReceive().addAccount(Arg.any());
|
||||
messagingService.didNotReceive().send(Arg.any());
|
||||
@@ -331,7 +331,7 @@ describe("Cipher Service", () => {
|
||||
|
||||
tokenService.getTwoFactorToken(email).resolves(twoFactorToken);
|
||||
|
||||
await authService.logIn(email, masterPassword, null);
|
||||
await authService.logIn(email, masterPassword);
|
||||
|
||||
apiService.received(1).postIdentityToken(
|
||||
Arg.is((actual) => {
|
||||
@@ -406,7 +406,7 @@ describe("Cipher Service", () => {
|
||||
tokenService.getTwoFactorToken(null).resolves(null);
|
||||
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
|
||||
// Api call:
|
||||
@@ -452,7 +452,7 @@ describe("Cipher Service", () => {
|
||||
tokenService.getTwoFactorToken(null).resolves(null);
|
||||
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
|
||||
cryptoService.didNotReceive().setEncPrivateKey(privateKey);
|
||||
@@ -466,7 +466,7 @@ describe("Cipher Service", () => {
|
||||
|
||||
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();
|
||||
keyConnectorService.received(1).getAndSetKey(keyConnectorUrl);
|
||||
@@ -500,7 +500,7 @@ describe("Cipher Service", () => {
|
||||
|
||||
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();
|
||||
cryptoService.received(1).setKey(preloginKey);
|
||||
@@ -529,7 +529,7 @@ describe("Cipher Service", () => {
|
||||
const tokenResponse = newTokenResponse();
|
||||
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(
|
||||
Arg.is((actual) => {
|
||||
|
||||
Reference in New Issue
Block a user