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

Merge branch 'main' into km/replace-encstring-with-unsigned-shared-key

This commit is contained in:
Bernd Schoolmann
2025-12-12 18:34:10 +01:00
committed by GitHub
330 changed files with 8886 additions and 4437 deletions

View File

@@ -113,20 +113,14 @@ export class LoginCommand {
} else if (options.sso != null && this.canInteract) {
// If the optional Org SSO Identifier isn't provided, the option value is `true`.
const orgSsoIdentifier = options.sso === true ? null : options.sso;
const passwordOptions: any = {
type: "password",
length: 64,
uppercase: true,
lowercase: true,
numbers: true,
special: false,
};
const state = await this.passwordGenerationService.generatePassword(passwordOptions);
ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256");
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
const ssoPromptData = await this.makeSsoPromptData();
ssoCodeVerifier = ssoPromptData.ssoCodeVerifier;
try {
const ssoParams = await this.openSsoPrompt(codeChallenge, state, orgSsoIdentifier);
const ssoParams = await this.openSsoPrompt(
ssoPromptData.codeChallenge,
ssoPromptData.state,
orgSsoIdentifier,
);
ssoCode = ssoParams.ssoCode;
orgIdentifier = ssoParams.orgIdentifier;
} catch {
@@ -231,9 +225,43 @@ export class LoginCommand {
new PasswordLoginCredentials(email, password, twoFactor),
);
}
// Begin Acting on initial AuthResult
if (response.requiresEncryptionKeyMigration) {
return Response.error(this.i18nService.t("legacyEncryptionUnsupported"));
}
// Opting for not checking feature flag since the server will not respond with
// SsoOrganizationIdentifier if the feature flag is not enabled.
if (response.requiresSso && this.canInteract) {
const ssoPromptData = await this.makeSsoPromptData();
ssoCodeVerifier = ssoPromptData.ssoCodeVerifier;
try {
const ssoParams = await this.openSsoPrompt(
ssoPromptData.codeChallenge,
ssoPromptData.state,
response.ssoOrganizationIdentifier,
);
ssoCode = ssoParams.ssoCode;
orgIdentifier = ssoParams.orgIdentifier;
if (ssoCode != null && ssoCodeVerifier != null) {
response = await this.loginStrategyService.logIn(
new SsoLoginCredentials(
ssoCode,
ssoCodeVerifier,
this.ssoRedirectUri,
orgIdentifier,
undefined, // email to look up 2FA token not required as CLI can't remember 2FA token
twoFactor,
),
);
}
} catch {
return Response.badRequest("Something went wrong. Try again.");
}
}
if (response.requiresTwoFactor) {
const twoFactorProviders = await this.twoFactorService.getSupportedProviders(null);
if (twoFactorProviders.length === 0) {
@@ -279,6 +307,10 @@ export class LoginCommand {
if (twoFactorToken == null && selectedProvider.type === TwoFactorProviderType.Email) {
const emailReq = new TwoFactorEmailRequest();
emailReq.email = await this.loginStrategyService.getEmail();
// if the user was logging in with SSO, we need to include the SSO session token
if (response.ssoEmail2FaSessionToken != null) {
emailReq.ssoEmail2FaSessionToken = response.ssoEmail2FaSessionToken;
}
emailReq.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash();
await this.twoFactorApiService.postTwoFactorEmail(emailReq);
}
@@ -324,6 +356,7 @@ export class LoginCommand {
response = await this.loginStrategyService.logInNewDeviceVerification(newDeviceToken);
}
// We check response two factor again here since MFA could fail based on the logic on ln 226
if (response.requiresTwoFactor) {
return Response.error("Login failed.");
}
@@ -692,6 +725,27 @@ export class LoginCommand {
};
}
/// Generate SSO prompt data: code verifier, code challenge, and state
private async makeSsoPromptData(): Promise<{
ssoCodeVerifier: string;
codeChallenge: string;
state: string;
}> {
const passwordOptions: any = {
type: "password",
length: 64,
uppercase: true,
lowercase: true,
numbers: true,
special: false,
};
const state = await this.passwordGenerationService.generatePassword(passwordOptions);
const ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256");
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
return { ssoCodeVerifier, codeChallenge, state };
}
private async openSsoPrompt(
codeChallenge: string,
state: string,

View File

@@ -15,6 +15,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { mockAccountInfoWith } from "@bitwarden/common/spec";
import { CsprngArray } from "@bitwarden/common/types/csprng";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
@@ -48,9 +49,10 @@ describe("UnlockCommand", () => {
const mockMasterPassword = "testExample";
const activeAccount: Account = {
id: "user-id" as UserId,
email: "user@example.com",
emailVerified: true,
name: "User",
...mockAccountInfoWith({
email: "user@example.com",
name: "User",
}),
};
const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
const mockSessionKey = new Uint8Array(64) as CsprngArray;

View File

@@ -492,10 +492,7 @@ export class ServiceContainer {
const pinStateService = new PinStateService(this.stateProvider);
this.pinService = new PinService(
this.accountService,
this.encryptService,
this.kdfConfigService,
this.keyGenerationService,
this.logService,
this.keyService,
this.sdkService,
@@ -908,7 +905,7 @@ export class ServiceContainer {
this.collectionService,
this.keyService,
this.encryptService,
this.pinService,
this.keyGenerationService,
this.accountService,
this.restrictedItemTypesService,
);
@@ -916,7 +913,7 @@ export class ServiceContainer {
this.individualExportService = new IndividualVaultExportService(
this.folderService,
this.cipherService,
this.pinService,
this.keyGenerationService,
this.keyService,
this.encryptService,
this.cryptoFunctionService,
@@ -930,7 +927,7 @@ export class ServiceContainer {
this.organizationExportService = new OrganizationVaultExportService(
this.cipherService,
this.vaultExportApiService,
this.pinService,
this.keyGenerationService,
this.keyService,
this.encryptService,
this.cryptoFunctionService,