1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-22 04:14:04 +00:00

Merge branch 'main' into anders/pnpm

This commit is contained in:
Anders Åberg
2025-12-19 08:54:22 +01:00
committed by GitHub
384 changed files with 29024 additions and 3925 deletions

View File

@@ -69,7 +69,7 @@
"browser-hrtime": "1.1.8",
"chalk": "4.1.2",
"commander": "14.0.0",
"core-js": "3.45.0",
"core-js": "3.47.0",
"form-data": "4.0.4",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",

View File

@@ -13,6 +13,7 @@ import {
SsoLoginCredentials,
SsoUrlService,
UserApiLoginCredentials,
UserDecryptionOptionsServiceAbstraction,
} from "@bitwarden/auth/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
@@ -82,6 +83,7 @@ export class LoginCommand {
protected ssoUrlService: SsoUrlService,
protected i18nService: I18nService,
protected masterPasswordService: MasterPasswordServiceAbstraction,
protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
protected encryptedMigrator: EncryptedMigrator,
) {}
@@ -361,11 +363,13 @@ export class LoginCommand {
return Response.error("Login failed.");
}
if (response.resetMasterPassword) {
return Response.error(
"In order to log in with SSO from the CLI, you must first log in" +
" through the web vault to set your master password.",
);
// If we are in the SSO flow and we got a successful login response (we are past rejection scenarios
// and should always have a userId here), validate that SSO user in MP encryption org has MP set
// This must be done here b/c we have 2 places we try to login with SSO above and neither has a
// common handleSsoAuthnResult method to consoldiate this logic into (1. the normal SSO flow and
// 2. the requiresSso automatic authentication flow)
if (ssoCode != null && ssoCodeVerifier != null && response.userId) {
await this.validateSsoUserInMpEncryptionOrgHasMp(response.userId);
}
// Check if Key Connector domain confirmation is required
@@ -836,4 +840,35 @@ export class LoginCommand {
const checkStateSplit = checkState.split("_identifier=");
return stateSplit[0] === checkStateSplit[0];
}
/**
* Validate that a user logging in with SSO that is in an org using MP encryption
* has a MP set. If not, they cannot set a MP in the CLI and must use another client.
* @param userId
* @returns void
*/
private async validateSsoUserInMpEncryptionOrgHasMp(userId: UserId): Promise<void> {
const userDecryptionOptions = await firstValueFrom(
this.userDecryptionOptionsService.userDecryptionOptionsById$(userId),
);
// device trust isn't supported in the CLI as we don't have persistent device key storage.
const notUsingTrustedDeviceEncryption = !userDecryptionOptions.trustedDeviceOption;
const notUsingKeyConnector = !userDecryptionOptions.keyConnectorOption;
if (
notUsingTrustedDeviceEncryption &&
notUsingKeyConnector &&
!userDecryptionOptions.hasMasterPassword
) {
// If user is in an org that is using MP encryption and they JIT provisioned but
// have not yet set a MP and come to the CLI to login, they won't be able to unlock
// or set a MP in the CLI as it isn't supported.
await this.logoutCallback();
throw Response.error(
"In order to log in with SSO from the CLI, you must first log in" +
" through the web vault, the desktop, or the extension to set your master password.",
);
}
}
}

View File

@@ -1,12 +1,12 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { firstValueFrom, map } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { UserId } from "@bitwarden/user-core";
import { Response } from "../models/response";
import { TemplateResponse } from "../models/response/template.response";
@@ -17,16 +17,17 @@ export class StatusCommand {
private syncService: SyncService,
private accountService: AccountService,
private authService: AuthService,
private userAutoUnlockKeyService: UserAutoUnlockKeyService,
) {}
async run(): Promise<Response> {
try {
const baseUrl = await this.baseUrl();
const status = await this.status();
const lastSync = await this.syncService.getLastSync();
const [userId, email] = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
);
const status = await this.status(userId);
return Response.success(
new TemplateResponse({
@@ -42,12 +43,18 @@ export class StatusCommand {
}
}
private async baseUrl(): Promise<string> {
private async baseUrl(): Promise<string | undefined> {
const env = await firstValueFrom(this.envService.environment$);
return env.getUrls().base;
}
private async status(): Promise<"unauthenticated" | "locked" | "unlocked"> {
private async status(
userId: UserId | undefined,
): Promise<"unauthenticated" | "locked" | "unlocked"> {
if (userId != null) {
await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(userId);
}
const authStatus = await this.authService.getAuthStatus();
if (authStatus === AuthenticationStatus.Unlocked) {
return "unlocked";

View File

@@ -122,6 +122,7 @@ export class OssServeConfigurator {
this.serviceContainer.syncService,
this.serviceContainer.accountService,
this.serviceContainer.authService,
this.serviceContainer.userAutoUnlockKeyService,
);
this.deleteCommand = new DeleteCommand(
this.serviceContainer.cipherService,

View File

@@ -195,6 +195,7 @@ export class Program extends BaseProgram {
this.serviceContainer.ssoUrlService,
this.serviceContainer.i18nService,
this.serviceContainer.masterPasswordService,
this.serviceContainer.userDecryptionOptionsService,
this.serviceContainer.encryptedMigrator,
);
const response = await command.run(email, password, options);
@@ -524,6 +525,7 @@ export class Program extends BaseProgram {
this.serviceContainer.syncService,
this.serviceContainer.accountService,
this.serviceContainer.authService,
this.serviceContainer.userAutoUnlockKeyService,
);
const response = await command.run();
this.processResponse(response);

View File

@@ -69,6 +69,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
import { HibpApiService } from "@bitwarden/common/dirt/services/hibp-api.service";
import { ClientType } from "@bitwarden/common/enums";
import { DefaultAccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/default-account-cryptographic-state.service";
import {
DefaultKeyGenerationService,
KeyGenerationService,
@@ -334,6 +335,7 @@ export class ServiceContainer {
masterPasswordUnlockService: MasterPasswordUnlockService;
cipherArchiveService: CipherArchiveService;
lockService: LockService;
private accountCryptographicStateService: DefaultAccountCryptographicStateService;
constructor() {
let p = null;
@@ -717,6 +719,10 @@ export class ServiceContainer {
this.accountService,
);
this.accountCryptographicStateService = new DefaultAccountCryptographicStateService(
this.stateProvider,
);
this.loginStrategyService = new LoginStrategyService(
this.accountService,
this.masterPasswordService,
@@ -744,6 +750,7 @@ export class ServiceContainer {
this.kdfConfigService,
this.taskSchedulerService,
this.configService,
this.accountCryptographicStateService,
);
this.restrictedItemTypesService = new RestrictedItemTypesService(
@@ -879,6 +886,7 @@ export class ServiceContainer {
this.stateProvider,
this.securityStateService,
this.kdfConfigService,
this.accountCryptographicStateService,
);
this.totpService = new TotpService(this.sdkService);
@@ -982,7 +990,12 @@ export class ServiceContainer {
this.masterPasswordApiService = new MasterPasswordApiService(this.apiService, this.logService);
const changeKdfApiService = new DefaultChangeKdfApiService(this.apiService);
const changeKdfService = new DefaultChangeKdfService(changeKdfApiService, this.sdkService);
const changeKdfService = new DefaultChangeKdfService(
changeKdfApiService,
this.sdkService,
this.keyService,
this.masterPasswordService,
);
this.encryptedMigrator = new DefaultEncryptedMigrator(
this.kdfConfigService,
changeKdfService,