mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-06 18:57:56 +01:00
[PM-2998] Move Approving Device Check (#5822)
* Switch to retrieving approving device from token response - Remove exist-by-types API call - Define `HasApprovingDevices` on TDE options * Update Naming * Update Test * Update Missing Names
This commit is contained in:
parent
0696252fb3
commit
c85ddfe833
@ -7,7 +7,6 @@ import {
|
||||
switchMap,
|
||||
Subject,
|
||||
catchError,
|
||||
forkJoin,
|
||||
from,
|
||||
of,
|
||||
finalize,
|
||||
@ -22,11 +21,6 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import {
|
||||
DesktopDeviceTypes,
|
||||
DeviceType,
|
||||
MobileDeviceTypes,
|
||||
} from "@bitwarden/common/enums/device-type.enum";
|
||||
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -170,23 +164,6 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
||||
loadUntrustedDeviceData(accountDecryptionOptions: AccountDecryptionOptions) {
|
||||
this.loading = true;
|
||||
|
||||
const mobileAndDesktopDeviceTypes: DeviceType[] = Array.from(MobileDeviceTypes).concat(
|
||||
Array.from(DesktopDeviceTypes)
|
||||
);
|
||||
|
||||
// Note: Each obs must handle error here and protect inner observable b/c we are using forkJoin below
|
||||
// as per RxJs docs: if any given observable errors at some point, then
|
||||
// forkJoin will error as well and immediately unsubscribe from the other observables.
|
||||
const mobileOrDesktopDevicesExistence$ = this.devicesService
|
||||
.getDevicesExistenceByTypes$(mobileAndDesktopDeviceTypes)
|
||||
.pipe(
|
||||
catchError((err: unknown) => {
|
||||
this.validationService.showError(err);
|
||||
return of(undefined);
|
||||
}),
|
||||
takeUntil(this.destroy$)
|
||||
);
|
||||
|
||||
const email$ = from(this.stateService.getEmail()).pipe(
|
||||
catchError((err: unknown) => {
|
||||
this.validationService.showError(err);
|
||||
@ -195,18 +172,16 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
||||
takeUntil(this.destroy$)
|
||||
);
|
||||
|
||||
forkJoin({
|
||||
mobileOrDesktopDevicesExistence: mobileOrDesktopDevicesExistence$,
|
||||
email: email$,
|
||||
})
|
||||
email$
|
||||
.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
finalize(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
)
|
||||
.subscribe(({ mobileOrDesktopDevicesExistence, email }) => {
|
||||
const showApproveFromOtherDeviceBtn = mobileOrDesktopDevicesExistence || false;
|
||||
.subscribe((email) => {
|
||||
const showApproveFromOtherDeviceBtn =
|
||||
accountDecryptionOptions?.trustedDeviceOption?.hasLoginApprovingDevice || false;
|
||||
|
||||
const showReqAdminApprovalBtn =
|
||||
!!accountDecryptionOptions?.trustedDeviceOption?.hasAdminApproval || false;
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { DeviceType } from "../../enums";
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
|
||||
import { DeviceResponse } from "./responses/device.response";
|
||||
@ -9,7 +8,6 @@ export abstract class DevicesApiServiceAbstraction {
|
||||
getDeviceByIdentifier: (deviceIdentifier: string) => Promise<DeviceResponse>;
|
||||
|
||||
getDevices: () => Promise<ListResponse<DeviceResponse>>;
|
||||
getDevicesExistenceByTypes: (deviceTypes: DeviceType[]) => Promise<boolean>;
|
||||
|
||||
updateTrustedDeviceKeys: (
|
||||
deviceIdentifier: string,
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { DeviceType } from "../../enums";
|
||||
|
||||
import { DeviceView } from "./views/device.view";
|
||||
|
||||
export abstract class DevicesServiceAbstraction {
|
||||
getDevices$: () => Observable<Array<DeviceView>>;
|
||||
getDevicesExistenceByTypes$: (deviceTypes: DeviceType[]) => Observable<boolean>;
|
||||
getDeviceByIdentifier$: (deviceIdentifier: string) => Observable<DeviceView>;
|
||||
isDeviceKnownForUser$: (email: string, deviceIdentifier: string) => Observable<boolean>;
|
||||
updateTrustedDeviceKeys$: (
|
||||
|
@ -148,6 +148,7 @@ describe("SsoLogInStrategy", () => {
|
||||
HasMasterPassword: true,
|
||||
TrustedDeviceOption: {
|
||||
HasAdminApproval: true,
|
||||
HasLoginApprovingDevice: true,
|
||||
EncryptedPrivateKey: mockEncDevicePrivateKey,
|
||||
EncryptedUserKey: mockEncUserKey,
|
||||
},
|
||||
|
@ -1,3 +1,3 @@
|
||||
export class TrustedDeviceUserDecryptionOption {
|
||||
constructor(public hasAdminApproval: boolean) {}
|
||||
constructor(public hasAdminApproval: boolean, public hasLoginApprovingDevice: boolean) {}
|
||||
}
|
||||
|
@ -3,12 +3,14 @@ import { EncString } from "../../../../platform/models/domain/enc-string";
|
||||
|
||||
export interface ITrustedDeviceUserDecryptionOptionServerResponse {
|
||||
HasAdminApproval: boolean;
|
||||
HasLoginApprovingDevice: boolean;
|
||||
EncryptedPrivateKey?: string;
|
||||
EncryptedUserKey?: string;
|
||||
}
|
||||
|
||||
export class TrustedDeviceUserDecryptionOptionResponse extends BaseResponse {
|
||||
hasAdminApproval: boolean;
|
||||
hasLoginApprovingDevice: boolean;
|
||||
encryptedPrivateKey: EncString;
|
||||
encryptedUserKey: EncString;
|
||||
|
||||
@ -16,6 +18,8 @@ export class TrustedDeviceUserDecryptionOptionResponse extends BaseResponse {
|
||||
super(response);
|
||||
this.hasAdminApproval = this.getResponseProperty("HasAdminApproval");
|
||||
|
||||
this.hasLoginApprovingDevice = this.getResponseProperty("HasLoginApprovingDevice");
|
||||
|
||||
if (response.EncryptedPrivateKey) {
|
||||
this.encryptedPrivateKey = new EncString(this.getResponseProperty("EncryptedPrivateKey"));
|
||||
}
|
||||
|
@ -322,7 +322,8 @@ export class AccountDecryptionOptions {
|
||||
|
||||
if (response.trustedDeviceOption) {
|
||||
accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption(
|
||||
response.trustedDeviceOption.hasAdminApproval
|
||||
response.trustedDeviceOption.hasAdminApproval,
|
||||
response.trustedDeviceOption.hasLoginApprovingDevice
|
||||
);
|
||||
}
|
||||
|
||||
@ -344,7 +345,8 @@ export class AccountDecryptionOptions {
|
||||
|
||||
if (obj.trustedDeviceOption) {
|
||||
accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption(
|
||||
obj.trustedDeviceOption.hasAdminApproval
|
||||
obj.trustedDeviceOption.hasAdminApproval,
|
||||
obj.trustedDeviceOption.hasLoginApprovingDevice
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { DevicesApiServiceAbstraction } from "../../abstractions/devices/devices-api.service.abstraction";
|
||||
import { DeviceResponse } from "../../abstractions/devices/responses/device.response";
|
||||
import { DeviceType } from "../../enums";
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
|
||||
@ -46,18 +45,6 @@ export class DevicesApiServiceImplementation implements DevicesApiServiceAbstrac
|
||||
return new ListResponse(r, DeviceResponse);
|
||||
}
|
||||
|
||||
async getDevicesExistenceByTypes(deviceTypes: DeviceType[]): Promise<boolean> {
|
||||
const r = await this.apiService.send(
|
||||
"POST",
|
||||
"/devices/exist-by-types",
|
||||
deviceTypes,
|
||||
true,
|
||||
true,
|
||||
null
|
||||
);
|
||||
return Boolean(r);
|
||||
}
|
||||
|
||||
async updateTrustedDeviceKeys(
|
||||
deviceIdentifier: string,
|
||||
devicePublicKeyEncryptedUserKey: string,
|
||||
|
@ -4,7 +4,6 @@ import { DevicesApiServiceAbstraction } from "../../abstractions/devices/devices
|
||||
import { DevicesServiceAbstraction } from "../../abstractions/devices/devices.service.abstraction";
|
||||
import { DeviceResponse } from "../../abstractions/devices/responses/device.response";
|
||||
import { DeviceView } from "../../abstractions/devices/views/device.view";
|
||||
import { DeviceType } from "../../enums";
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
|
||||
/**
|
||||
@ -31,13 +30,6 @@ export class DevicesServiceImplementation implements DevicesServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns whether the user has any devices of the specified types.
|
||||
*/
|
||||
getDevicesExistenceByTypes$(deviceTypes: DeviceType[]): Observable<boolean> {
|
||||
return defer(() => this.devicesApiService.getDevicesExistenceByTypes(deviceTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Gets the device with the specified identifier.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user