// 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 | 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 | 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 | 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 { 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; } }