From 82903fdab73e9d05c72df23890ca1cb731bbb7fa Mon Sep 17 00:00:00 2001 From: Todd Martin Date: Tue, 18 Mar 2025 16:07:37 -0400 Subject: [PATCH] Moved methods --- .../src/vault/app/vault/vault.component.ts | 6 ++-- .../src/services/jslib-services.module.ts | 3 +- .../login-approval.component.ts | 4 +-- .../abstractions/auth-request-api.service.ts | 20 +++++++++++++ .../login-strategies/sso-login.strategy.ts | 7 +++-- .../auth-request/auth-request-api.service.ts | 28 +++++++++++++++++ .../auth-request/auth-request.service.spec.ts | 5 ++-- .../auth-request/auth-request.service.ts | 8 ++--- .../login-strategy.service.ts | 8 ++++- libs/common/src/abstractions/api.service.ts | 7 +---- .../request/auth-request-update.request.ts | 12 ++++++++ .../request/passwordless-auth.request.ts | 8 ----- libs/common/src/services/api.service.ts | 30 ------------------- 13 files changed, 87 insertions(+), 59 deletions(-) create mode 100644 libs/common/src/auth/models/request/auth-request-update.request.ts delete mode 100644 libs/common/src/auth/models/request/passwordless-auth.request.ts diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index 2c669e388f8..3f241a267f1 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -16,7 +16,7 @@ import { filter, first, map, take } from "rxjs/operators"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestApiService } from "@bitwarden/auth/common"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -110,7 +110,7 @@ export class VaultComponent implements OnInit, OnDestroy { private totpService: TotpService, private passwordRepromptService: PasswordRepromptService, private searchBarService: SearchBarService, - private apiService: ApiService, + private authRequestApiService: AuthRequestApiService, private dialogService: DialogService, private billingAccountProfileStateService: BillingAccountProfileStateService, private toastService: ToastService, @@ -231,7 +231,7 @@ export class VaultComponent implements OnInit, OnDestroy { this.searchBarService.setEnabled(true); this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); - const authRequest = await this.apiService.getLastAuthRequest(); + const authRequest = await this.authRequestApiService.getLastAuthRequest(); if (authRequest != null) { this.messagingService.send("openLoginApproval", { notificationId: authRequest.id, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 04bcb42aa53..acdadc87c8b 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -472,6 +472,7 @@ const safeProviders: SafeProvider[] = [ VaultTimeoutSettingsService, KdfConfigService, TaskSchedulerService, + AuthRequestApiService, ], }), safeProvider({ @@ -1157,7 +1158,7 @@ const safeProviders: SafeProvider[] = [ InternalMasterPasswordServiceAbstraction, KeyService, EncryptService, - ApiServiceAbstraction, + AuthRequestApiService, StateProvider, ], }), diff --git a/libs/auth/src/angular/login-approval/login-approval.component.ts b/libs/auth/src/angular/login-approval/login-approval.component.ts index 54d90306e5c..faff08fda4a 100644 --- a/libs/auth/src/angular/login-approval/login-approval.component.ts +++ b/libs/auth/src/angular/login-approval/login-approval.component.ts @@ -7,10 +7,10 @@ import { Subject, firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { + AuthRequestApiService, AuthRequestServiceAbstraction, LoginApprovalComponentServiceAbstraction as LoginApprovalComponentService, } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; @@ -59,7 +59,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { protected accountService: AccountService, protected platformUtilsService: PlatformUtilsService, protected i18nService: I18nService, - protected apiService: ApiService, + protected apiService: AuthRequestApiService, protected appIdService: AppIdService, protected keyService: KeyService, private dialogRef: DialogRef, diff --git a/libs/auth/src/common/abstractions/auth-request-api.service.ts b/libs/auth/src/common/abstractions/auth-request-api.service.ts index 1b0befc0df4..2617bc939ab 100644 --- a/libs/auth/src/common/abstractions/auth-request-api.service.ts +++ b/libs/auth/src/common/abstractions/auth-request-api.service.ts @@ -1,3 +1,4 @@ +import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request"; import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; @@ -34,4 +35,23 @@ export abstract class AuthRequestApiService { * @returns A promise that resolves to the auth request response. */ abstract postAuthRequest: (request: AuthRequest) => Promise; + + /** + * Updates an auth request by its ID, which is used to approve or deny the request. + * + * @param id The ID of the auth request. + * @param request The auth request update, indicating whether the request is approved or denied. + * @returns A promise that resolves to the auth request response. + */ + abstract putAuthRequest: ( + id: string, + request: AuthRequestUpdateRequest, + ) => Promise; + + /** + * Gets the most-recently-created auth request for the logged-in user. + * + * @returns A promise that resolves to the auth request response. + */ + abstract getLastAuthRequest: () => Promise; } diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.ts index 1dd01d6fc75..91f09288c1d 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.ts @@ -14,7 +14,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { UserId } from "@bitwarden/common/types/guid"; -import { AuthRequestServiceAbstraction } from "../abstractions"; +import { AuthRequestApiService, AuthRequestServiceAbstraction } from "../abstractions"; import { SsoLoginCredentials } from "../models/domain/login-credentials"; import { CacheData } from "../services/login-strategies/login-strategy.state"; @@ -72,6 +72,7 @@ export class SsoLoginStrategy extends LoginStrategy { private deviceTrustService: DeviceTrustServiceAbstraction, private authRequestService: AuthRequestServiceAbstraction, private i18nService: I18nService, + private authRequestApiService: AuthRequestApiService, ...sharedDeps: ConstructorParameters ) { super(...sharedDeps); @@ -230,7 +231,9 @@ export class SsoLoginStrategy extends LoginStrategy { let adminAuthReqResponse: AuthRequestResponse; try { - adminAuthReqResponse = await this.apiService.getAuthRequest(adminAuthReqStorable.id); + adminAuthReqResponse = await this.authRequestApiService.getAuthRequest( + adminAuthReqStorable.id, + ); } catch (error) { if (error instanceof ErrorResponse && error.statusCode === HttpStatusCode.NotFound) { // if we get a 404, it means the auth request has been deleted so clear it from storage diff --git a/libs/auth/src/common/services/auth-request/auth-request-api.service.ts b/libs/auth/src/common/services/auth-request/auth-request-api.service.ts index c9fec1400c9..2d01f073984 100644 --- a/libs/auth/src/common/services/auth-request/auth-request-api.service.ts +++ b/libs/auth/src/common/services/auth-request/auth-request-api.service.ts @@ -1,6 +1,8 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request"; import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { AuthRequestApiService } from "../../abstractions/auth-request-api.service"; @@ -75,4 +77,30 @@ export class DefaultAuthRequestApiService implements AuthRequestApiService { throw e; } } + + async putAuthRequest( + id: string, + request: AuthRequestUpdateRequest, + ): Promise { + const path = `/auth-requests/${id}`; + const r = await this.apiService.send("PUT", path, request, true, true); + return new AuthRequestResponse(r); + } + + async getAuthRequests(): Promise> { + const path = `/auth-requests/`; + const r = await this.apiService.send("GET", path, null, true, true); + return new ListResponse(r, AuthRequestResponse); + } + + async getLastAuthRequest(): Promise { + const requests = await this.getAuthRequests(); + const activeRequests = requests.data.filter( + (m: AuthRequestResponse) => !m.isAnswered && !m.isExpired, + ); + const lastRequest = activeRequests.sort((a: AuthRequestResponse, b: AuthRequestResponse) => + a.creationDate.localeCompare(b.creationDate), + )[activeRequests.length - 1]; + return lastRequest; + } } diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts index 1e9c46db0ee..38a588da3ea 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts @@ -1,6 +1,5 @@ import { mock } from "jest-mock-extended"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service"; @@ -15,6 +14,8 @@ import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; import { KeyService } from "@bitwarden/key-management"; +import { AuthRequestApiService } from "../../abstractions"; + import { AuthRequestService } from "./auth-request.service"; describe("AuthRequestService", () => { @@ -26,7 +27,7 @@ describe("AuthRequestService", () => { const appIdService = mock(); const keyService = mock(); const encryptService = mock(); - const apiService = mock(); + const apiService = mock(); let mockPrivateKey: Uint8Array; let mockPublicKey: Uint8Array; diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.ts b/libs/auth/src/common/services/auth-request/auth-request.service.ts index 1da5d2f1882..cb1f54c7d8c 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.ts @@ -3,10 +3,9 @@ import { Observable, Subject, firstValueFrom } from "rxjs"; import { Jsonify } from "type-fest"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable"; -import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request"; +import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; @@ -24,6 +23,7 @@ import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; import { KeyService } from "@bitwarden/key-management"; +import { AuthRequestApiService } from "../../abstractions"; import { AuthRequestServiceAbstraction } from "../../abstractions/auth-request.service.abstraction"; /** @@ -53,7 +53,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { private masterPasswordService: InternalMasterPasswordServiceAbstraction, private keyService: KeyService, private encryptService: EncryptService, - private apiService: ApiService, + private apiService: AuthRequestApiService, private stateProvider: StateProvider, ) { this.authRequestPushNotification$ = this.authRequestPushNotificationSubject.asObservable(); @@ -124,7 +124,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { const encryptedKey = await this.encryptService.rsaEncrypt(keyToEncrypt, pubKey); - const response = new PasswordlessAuthRequest( + const response = new AuthRequestUpdateRequest( encryptedKey.encryptedString, encryptedMasterKeyHash?.encryptedString, await this.appIdService.getAppId(), diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index 2adea85a9ec..448aca0d153 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -45,7 +45,11 @@ import { KdfConfigService, } from "@bitwarden/key-management"; -import { AuthRequestServiceAbstraction, LoginStrategyServiceAbstraction } from "../../abstractions"; +import { + AuthRequestApiService, + AuthRequestServiceAbstraction, + LoginStrategyServiceAbstraction, +} from "../../abstractions"; import { InternalUserDecryptionOptionsServiceAbstraction } from "../../abstractions/user-decryption-options.service.abstraction"; import { AuthRequestLoginStrategy, @@ -131,6 +135,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { protected vaultTimeoutSettingsService: VaultTimeoutSettingsService, protected kdfConfigService: KdfConfigService, protected taskSchedulerService: TaskSchedulerService, + protected authRequestApiService: AuthRequestApiService, ) { this.currentAuthnTypeState = this.stateProvider.get(CURRENT_LOGIN_STRATEGY_KEY); this.loginStrategyCacheState = this.stateProvider.get(CACHE_KEY); @@ -426,6 +431,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { this.deviceTrustService, this.authRequestService, this.i18nService, + this.authRequestApiService, ...sharedDeps, ); case AuthenticationType.UserApiKey: diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index cfef1faa7ba..1b9274aaba6 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -47,7 +47,7 @@ import { SsoTokenRequest } from "../auth/models/request/identity-token/sso-token import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-api-token.request"; import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request"; import { PasswordHintRequest } from "../auth/models/request/password-hint.request"; -import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request"; +import { AuthRequestUpdateRequest } from "../auth/models/request/auth-request-update.request"; import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request"; import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request"; import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request"; @@ -183,11 +183,6 @@ export abstract class ApiService { postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise; postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise; postConvertToKeyConnector: () => Promise; - //passwordless - getAuthRequest: (id: string) => Promise; - putAuthRequest: (id: string, request: PasswordlessAuthRequest) => Promise; - getAuthRequests: () => Promise>; - getLastAuthRequest: () => Promise; getUserBillingHistory: () => Promise; getUserBillingPayment: () => Promise; diff --git a/libs/common/src/auth/models/request/auth-request-update.request.ts b/libs/common/src/auth/models/request/auth-request-update.request.ts new file mode 100644 index 00000000000..377f442b29c --- /dev/null +++ b/libs/common/src/auth/models/request/auth-request-update.request.ts @@ -0,0 +1,12 @@ +/** + * Represents a request to update an AuthRequest with either approval or denial of the request. + * If the request is approved, the update will contain the key and/or hash to be shared with the requesting device. + */ +export class AuthRequestUpdateRequest { + constructor( + readonly key: string, + readonly masterPasswordHash: string, + readonly deviceIdentifier: string, + readonly requestApproved: boolean, + ) {} +} diff --git a/libs/common/src/auth/models/request/passwordless-auth.request.ts b/libs/common/src/auth/models/request/passwordless-auth.request.ts deleted file mode 100644 index 32ebbf8197e..00000000000 --- a/libs/common/src/auth/models/request/passwordless-auth.request.ts +++ /dev/null @@ -1,8 +0,0 @@ -export class PasswordlessAuthRequest { - constructor( - readonly key: string, - readonly masterPasswordHash: string, - readonly deviceIdentifier: string, - readonly requestApproved: boolean, - ) {} -} diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index db4e5fdbc8f..cda581179ce 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -55,7 +55,6 @@ import { TokenTwoFactorRequest } from "../auth/models/request/identity-token/tok import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-api-token.request"; import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request"; import { PasswordHintRequest } from "../auth/models/request/password-hint.request"; -import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request"; import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request"; import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request"; import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request"; @@ -68,7 +67,6 @@ import { UpdateTwoFactorWebAuthnDeleteRequest } from "../auth/models/request/upd import { UpdateTwoFactorWebAuthnRequest } from "../auth/models/request/update-two-factor-web-authn.request"; import { UpdateTwoFactorYubikeyOtpRequest } from "../auth/models/request/update-two-factor-yubikey-otp.request"; import { ApiKeyResponse } from "../auth/models/response/api-key.response"; -import { AuthRequestResponse } from "../auth/models/response/auth-request.response"; import { DeviceVerificationResponse } from "../auth/models/response/device-verification.response"; import { IdentityCaptchaResponse } from "../auth/models/response/identity-captcha.response"; import { IdentityDeviceVerificationResponse } from "../auth/models/response/identity-device-verification.response"; @@ -273,34 +271,6 @@ export class ApiService implements ApiServiceAbstraction { } } - // TODO: PM-3519: Create and move to AuthRequest Api service - async getAuthRequest(id: string): Promise { - const path = `/auth-requests/${id}`; - const r = await this.send("GET", path, null, true, true); - return new AuthRequestResponse(r); - } - - async putAuthRequest(id: string, request: PasswordlessAuthRequest): Promise { - const path = `/auth-requests/${id}`; - const r = await this.send("PUT", path, request, true, true); - return new AuthRequestResponse(r); - } - - async getAuthRequests(): Promise> { - const path = `/auth-requests/`; - const r = await this.send("GET", path, null, true, true); - return new ListResponse(r, AuthRequestResponse); - } - - async getLastAuthRequest(): Promise { - const requests = await this.getAuthRequests(); - const activeRequests = requests.data.filter((m) => !m.isAnswered && !m.isExpired); - const lastRequest = activeRequests.sort((a: AuthRequestResponse, b: AuthRequestResponse) => - a.creationDate.localeCompare(b.creationDate), - )[activeRequests.length - 1]; - return lastRequest; - } - // Account APIs async getProfile(): Promise {