mirror of
https://github.com/bitwarden/browser
synced 2026-02-19 19:04:01 +00:00
* PM-13632: Enable sign in with passkeys in the browser extension * Refactor component + Icon fix This commit refactors the login-via-webauthn commit as per @JaredSnider-Bitwarden suggestions. It also fixes an existing issue where Icons are not displayed properly on the web vault. Remove old one. Rename the file Working refactor Removed the icon from the component Fixed icons not showing. Changed layout to be 'embedded' * Add tracking links * Update app.module.ts * Remove default Icons on load * Remove login.module.ts * Add env changer to the passkey component * Remove leftover dependencies * PRF Unlock Cleanup and testes * Workaround prf type missing * Fix any type * Undo accidental cleanup to keep PR focused * Undo accidental cleanup to keep PR focused * Cleaned up public interface * Use UserId type * Typed UserId and improved isPrfUnlockAvailable * Rename key and use zero challenge array * logservice * Cleanup rpId handling * Refactor to separate component + icon * Moved the prf unlock service impl. * Fix broken test * fix tests * Use isChromium * Update services.module.ts * missing , in locales * Update desktop-lock-component.service.ts * Fix more desktoptests * Expect a single UnlockOption from IdTokenResponse, but multiple from sync * Missing s * remove catches * Use new control flow in unlock-via-prf.component.ts Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Changed throw behaviour of unlockVaultWithPrf * remove timeout comment * refactired webauthm-prf-unlock.service internally * WebAuthnPrfUnlockServiceAbstraction -> WebAuthnPrfUnlockService * Fixed any and bad import * Fix errors after merge * Added missing PinServiceAbstraction * Fixed format * Removed @Inject() * Fix broken tests after Inject removal * Return userkey instead of setting it * Used input/output signals * removed duplicate MessageSender registration * nit: Made import relative * Disable onPush requirement because it would need refactoring the component * Added feature flag (#17494) * Fixed ById from main * Import feature flag from file * Add missing test providers for MasterPasswordLockComponent Add WebAuthnPrfUnlockService and DialogService mocks to fix test failures caused by UnlockViaPrfComponent dependencies. --------- Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
240 lines
9.9 KiB
TypeScript
240 lines
9.9 KiB
TypeScript
// FIXME: Update this file to be type safe and remove this and next line
|
|
// @ts-strict-ignore
|
|
import { Jsonify } from "type-fest";
|
|
|
|
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
|
import { KeyConnectorUserDecryptionOptionResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/key-connector-user-decryption-option.response";
|
|
import { TrustedDeviceUserDecryptionOptionResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/trusted-device-user-decryption-option.response";
|
|
import { WebAuthnPrfDecryptionOptionResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/webauthn-prf-decryption-option.response";
|
|
|
|
/**
|
|
* Key Connector decryption options. Intended to be sent to the client for use after authentication.
|
|
* @see {@link UserDecryptionOptions}
|
|
*/
|
|
export class KeyConnectorUserDecryptionOption {
|
|
/** The URL of the key connector configured for this user. */
|
|
keyConnectorUrl: string;
|
|
|
|
/**
|
|
* Initializes a new instance of the KeyConnectorUserDecryptionOption from a response object.
|
|
* @param response The key connector user decryption option response object.
|
|
* @returns A new instance of the KeyConnectorUserDecryptionOption or undefined if `response` is nullish.
|
|
*/
|
|
static fromResponse(
|
|
response: KeyConnectorUserDecryptionOptionResponse,
|
|
): KeyConnectorUserDecryptionOption | undefined {
|
|
if (response == null) {
|
|
return undefined;
|
|
}
|
|
const options = new KeyConnectorUserDecryptionOption();
|
|
options.keyConnectorUrl = response?.keyConnectorUrl ?? null;
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Initializes a new instance of a KeyConnectorUserDecryptionOption from a JSON object.
|
|
* @param obj JSON object to deserialize.
|
|
* @returns A new instance of the KeyConnectorUserDecryptionOption or undefined if `obj` is nullish.
|
|
*/
|
|
static fromJSON(
|
|
obj: Jsonify<KeyConnectorUserDecryptionOption>,
|
|
): KeyConnectorUserDecryptionOption | undefined {
|
|
if (obj == null) {
|
|
return undefined;
|
|
}
|
|
return Object.assign(new KeyConnectorUserDecryptionOption(), obj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trusted device decryption options. Intended to be sent to the client for use after authentication.
|
|
* @see {@link UserDecryptionOptions}
|
|
*/
|
|
/**
|
|
* WebAuthn PRF decryption options. Intended to be sent to the client for use after authentication.
|
|
* @see {@link UserDecryptionOptions}
|
|
*/
|
|
export class WebAuthnPrfUserDecryptionOption {
|
|
/** The encrypted private key that can be decrypted with the PRF key. */
|
|
encryptedPrivateKey: string;
|
|
/** The encrypted user key that can be decrypted with the private key. */
|
|
encryptedUserKey: string;
|
|
/** The credential ID for this WebAuthn PRF credential. */
|
|
credentialId: string;
|
|
/** The transports supported by this credential. */
|
|
transports: string[];
|
|
|
|
/**
|
|
* Initializes a new instance of the WebAuthnPrfUserDecryptionOption from a response object.
|
|
* @param response The WebAuthn PRF user decryption option response object.
|
|
* @returns A new instance of the WebAuthnPrfUserDecryptionOption or undefined if `response` is nullish.
|
|
*/
|
|
static fromResponse(
|
|
response: WebAuthnPrfDecryptionOptionResponse,
|
|
): WebAuthnPrfUserDecryptionOption | undefined {
|
|
if (response == null) {
|
|
return undefined;
|
|
}
|
|
if (!response.encryptedPrivateKey || !response.encryptedUserKey) {
|
|
return undefined;
|
|
}
|
|
const options = new WebAuthnPrfUserDecryptionOption();
|
|
options.encryptedPrivateKey = response.encryptedPrivateKey.encryptedString;
|
|
options.encryptedUserKey = response.encryptedUserKey.encryptedString;
|
|
options.credentialId = response.credentialId;
|
|
options.transports = response.transports || [];
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Initializes a new instance of a WebAuthnPrfUserDecryptionOption from a JSON object.
|
|
* @param obj JSON object to deserialize.
|
|
* @returns A new instance of the WebAuthnPrfUserDecryptionOption or undefined if `obj` is nullish.
|
|
*/
|
|
static fromJSON(
|
|
obj: Jsonify<WebAuthnPrfUserDecryptionOption>,
|
|
): WebAuthnPrfUserDecryptionOption | undefined {
|
|
if (obj == null) {
|
|
return undefined;
|
|
}
|
|
return Object.assign(new WebAuthnPrfUserDecryptionOption(), obj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trusted device decryption options. Intended to be sent to the client for use after authentication.
|
|
* @see {@link UserDecryptionOptions}
|
|
*/
|
|
export class TrustedDeviceUserDecryptionOption {
|
|
/** True if an admin has approved an admin auth request previously made from this device. */
|
|
hasAdminApproval: boolean;
|
|
/** True if the user has a device capable of approving an auth request. */
|
|
hasLoginApprovingDevice: boolean;
|
|
/** True if the user has manage reset password permission, as these users must be forced to have a master password. */
|
|
hasManageResetPasswordPermission: boolean;
|
|
/** True if tde is disabled but user has not set a master password yet. */
|
|
isTdeOffboarding: boolean;
|
|
|
|
/**
|
|
* Initializes a new instance of the TrustedDeviceUserDecryptionOption from a response object.
|
|
* @param response The trusted device user decryption option response object.
|
|
* @returns A new instance of the TrustedDeviceUserDecryptionOption or undefined if `response` is nullish.
|
|
*/
|
|
static fromResponse(
|
|
response: TrustedDeviceUserDecryptionOptionResponse,
|
|
): TrustedDeviceUserDecryptionOption | undefined {
|
|
if (response == null) {
|
|
return undefined;
|
|
}
|
|
const options = new TrustedDeviceUserDecryptionOption();
|
|
options.hasAdminApproval = response?.hasAdminApproval ?? false;
|
|
options.hasLoginApprovingDevice = response?.hasLoginApprovingDevice ?? false;
|
|
options.hasManageResetPasswordPermission = response?.hasManageResetPasswordPermission ?? false;
|
|
options.isTdeOffboarding = response?.isTdeOffboarding ?? false;
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Initializes a new instance of the TrustedDeviceUserDecryptionOption from a JSON object.
|
|
* @param obj JSON object to deserialize.
|
|
* @returns A new instance of the TrustedDeviceUserDecryptionOption or undefined if `obj` is nullish.
|
|
*/
|
|
static fromJSON(
|
|
obj: Jsonify<TrustedDeviceUserDecryptionOption>,
|
|
): TrustedDeviceUserDecryptionOption | undefined {
|
|
if (obj == null) {
|
|
return undefined;
|
|
}
|
|
return Object.assign(new TrustedDeviceUserDecryptionOption(), obj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Represents the decryption options the user has configured on the server. This is intended to be sent
|
|
* to the client on authentication, and can be used to determine how to decrypt the user's vault.
|
|
*/
|
|
export class UserDecryptionOptions {
|
|
/** True if the user has a master password configured on the server. */
|
|
hasMasterPassword: boolean;
|
|
/** {@link TrustedDeviceUserDecryptionOption} */
|
|
trustedDeviceOption?: TrustedDeviceUserDecryptionOption;
|
|
/** {@link KeyConnectorUserDecryptionOption} */
|
|
keyConnectorOption?: KeyConnectorUserDecryptionOption;
|
|
/** Array of {@link WebAuthnPrfUserDecryptionOption} */
|
|
webAuthnPrfOptions?: WebAuthnPrfUserDecryptionOption[];
|
|
|
|
/**
|
|
* Initializes a new instance of the UserDecryptionOptions from a response object.
|
|
* @param response user decryption options response object
|
|
* @returns A new instance of the UserDecryptionOptions.
|
|
* @throws If the response is nullish, this method will throw an error. User decryption options
|
|
* are required for client initialization.
|
|
*/
|
|
static fromIdentityTokenResponse(response: IdentityTokenResponse): UserDecryptionOptions {
|
|
if (response == null) {
|
|
throw new Error(
|
|
"User Decryption Options are required for client initialization. Response is nullish.",
|
|
);
|
|
}
|
|
|
|
const decryptionOptions = new UserDecryptionOptions();
|
|
|
|
if (response.userDecryptionOptions) {
|
|
// If the response has userDecryptionOptions, this means it's on a post-TDE server version and can interrogate
|
|
// the new decryption options.
|
|
const responseOptions = response.userDecryptionOptions;
|
|
decryptionOptions.hasMasterPassword = responseOptions.hasMasterPassword;
|
|
|
|
decryptionOptions.trustedDeviceOption = TrustedDeviceUserDecryptionOption.fromResponse(
|
|
responseOptions.trustedDeviceOption,
|
|
);
|
|
|
|
decryptionOptions.keyConnectorOption = KeyConnectorUserDecryptionOption.fromResponse(
|
|
responseOptions.keyConnectorOption,
|
|
);
|
|
|
|
// The IdTokenResponse only returns a single WebAuthn PRF option to support immediate unlock after logging in
|
|
// with the same PRF passkey.
|
|
// Since our domain model supports multiple WebAuthn PRF options, we convert the single option into an array.
|
|
if (responseOptions.webAuthnPrfOption) {
|
|
const option = WebAuthnPrfUserDecryptionOption.fromResponse(
|
|
responseOptions.webAuthnPrfOption,
|
|
);
|
|
if (option) {
|
|
decryptionOptions.webAuthnPrfOptions = [option];
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(
|
|
"User Decryption Options are required for client initialization. userDecryptionOptions is missing in response.",
|
|
);
|
|
}
|
|
return decryptionOptions;
|
|
}
|
|
|
|
/**
|
|
* Initializes a new instance of the UserDecryptionOptions from a JSON object.
|
|
* @param obj JSON object to deserialize.
|
|
* @returns A new instance of the UserDecryptionOptions. Will initialize even if the JSON object is nullish.
|
|
*/
|
|
static fromJSON(obj: Jsonify<UserDecryptionOptions>): UserDecryptionOptions {
|
|
const decryptionOptions = Object.assign(new UserDecryptionOptions(), obj);
|
|
|
|
decryptionOptions.trustedDeviceOption = TrustedDeviceUserDecryptionOption.fromJSON(
|
|
obj?.trustedDeviceOption,
|
|
);
|
|
|
|
decryptionOptions.keyConnectorOption = KeyConnectorUserDecryptionOption.fromJSON(
|
|
obj?.keyConnectorOption,
|
|
);
|
|
|
|
if (obj?.webAuthnPrfOptions && Array.isArray(obj.webAuthnPrfOptions)) {
|
|
decryptionOptions.webAuthnPrfOptions = obj.webAuthnPrfOptions
|
|
.map((option) => WebAuthnPrfUserDecryptionOption.fromJSON(option))
|
|
.filter((option) => option !== undefined);
|
|
}
|
|
|
|
return decryptionOptions;
|
|
}
|
|
}
|