diff --git a/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts deleted file mode 100644 index ce6c0d78f0..0000000000 --- a/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; -import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; - -import { - CryptoServiceInitOptions, - cryptoServiceFactory, -} from "../../../platform/background/service-factories/crypto-service.factory"; -import { - CachedServices, - FactoryOptions, - factory, -} from "../../../platform/background/service-factories/factory-options"; - -type AuthRequestCryptoServiceFactoryOptions = FactoryOptions; - -export type AuthRequestCryptoServiceInitOptions = AuthRequestCryptoServiceFactoryOptions & - CryptoServiceInitOptions; - -export function authRequestCryptoServiceFactory( - cache: { authRequestCryptoService?: AuthRequestCryptoServiceAbstraction } & CachedServices, - opts: AuthRequestCryptoServiceInitOptions, -): Promise { - return factory( - cache, - "authRequestCryptoService", - opts, - async () => new AuthRequestCryptoServiceImplementation(await cryptoServiceFactory(cache, opts)), - ); -} diff --git a/apps/browser/src/auth/background/service-factories/auth-request-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-request-service.factory.ts new file mode 100644 index 0000000000..bd96a211ba --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/auth-request-service.factory.ts @@ -0,0 +1,49 @@ +import { AuthRequestService, AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; + +import { + apiServiceFactory, + ApiServiceInitOptions, +} from "../../../platform/background/service-factories/api-service.factory"; +import { + appIdServiceFactory, + AppIdServiceInitOptions, +} from "../../../platform/background/service-factories/app-id-service.factory"; +import { + CryptoServiceInitOptions, + cryptoServiceFactory, +} from "../../../platform/background/service-factories/crypto-service.factory"; +import { + CachedServices, + FactoryOptions, + factory, +} from "../../../platform/background/service-factories/factory-options"; +import { + stateServiceFactory, + StateServiceInitOptions, +} from "../../../platform/background/service-factories/state-service.factory"; + +type AuthRequestServiceFactoryOptions = FactoryOptions; + +export type AuthRequestServiceInitOptions = AuthRequestServiceFactoryOptions & + AppIdServiceInitOptions & + CryptoServiceInitOptions & + ApiServiceInitOptions & + StateServiceInitOptions; + +export function authRequestServiceFactory( + cache: { authRequestService?: AuthRequestServiceAbstraction } & CachedServices, + opts: AuthRequestServiceInitOptions, +): Promise { + return factory( + cache, + "authRequestService", + opts, + async () => + new AuthRequestService( + await appIdServiceFactory(cache, opts), + await cryptoServiceFactory(cache, opts), + await apiServiceFactory(cache, opts), + await stateServiceFactory(cache, opts), + ), + ); +} diff --git a/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts b/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts index 39a31ce3d0..ecd695f373 100644 --- a/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts @@ -52,9 +52,9 @@ import { } from "../../../tools/background/service_factories/password-strength-service.factory"; import { - authRequestCryptoServiceFactory, - AuthRequestCryptoServiceInitOptions, -} from "./auth-request-crypto-service.factory"; + authRequestServiceFactory, + AuthRequestServiceInitOptions, +} from "./auth-request-service.factory"; import { deviceTrustCryptoServiceFactory, DeviceTrustCryptoServiceInitOptions, @@ -84,7 +84,7 @@ export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions PolicyServiceInitOptions & PasswordStrengthServiceInitOptions & DeviceTrustCryptoServiceInitOptions & - AuthRequestCryptoServiceInitOptions; + AuthRequestServiceInitOptions; export function loginStrategyServiceFactory( cache: { loginStrategyService?: LoginStrategyServiceAbstraction } & CachedServices, @@ -112,7 +112,7 @@ export function loginStrategyServiceFactory( await passwordStrengthServiceFactory(cache, opts), await policyServiceFactory(cache, opts), await deviceTrustCryptoServiceFactory(cache, opts), - await authRequestCryptoServiceFactory(cache, opts), + await authRequestServiceFactory(cache, opts), ), ); } diff --git a/apps/browser/src/auth/popup/login-via-auth-request.component.ts b/apps/browser/src/auth/popup/login-via-auth-request.component.ts index 46511a5c34..a22636389a 100644 --- a/apps/browser/src/auth/popup/login-via-auth-request.component.ts +++ b/apps/browser/src/auth/popup/login-via-auth-request.component.ts @@ -3,10 +3,12 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component"; -import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; +import { + AuthRequestServiceAbstraction, + LoginStrategyServiceAbstraction, +} from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; @@ -48,7 +50,7 @@ export class LoginViaAuthRequestComponent loginService: LoginService, syncService: SyncService, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, - authReqCryptoService: AuthRequestCryptoServiceAbstraction, + authRequestService: AuthRequestServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction, private location: Location, ) { @@ -69,7 +71,7 @@ export class LoginViaAuthRequestComponent stateService, loginService, deviceTrustCryptoService, - authReqCryptoService, + authRequestService, loginStrategyService, ); super.onSuccessfulLogin = async () => { diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 7bc6228738..b2240702f0 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -5,6 +5,8 @@ import { PinCryptoService, LoginStrategyServiceAbstraction, LoginStrategyService, + AuthRequestServiceAbstraction, + AuthRequestService, } from "@bitwarden/auth/common"; import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; @@ -22,7 +24,6 @@ import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; @@ -35,7 +36,6 @@ import { UserVerificationService as UserVerificationServiceAbstraction } from "@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; -import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; @@ -274,7 +274,7 @@ export default class MainBackground { devicesApiService: DevicesApiServiceAbstraction; devicesService: DevicesServiceAbstraction; deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; - authRequestCryptoService: AuthRequestCryptoServiceAbstraction; + authRequestService: AuthRequestServiceAbstraction; accountService: AccountServiceAbstraction; globalStateProvider: GlobalStateProvider; pinCryptoService: PinCryptoServiceAbstraction; @@ -531,7 +531,12 @@ export default class MainBackground { this.devicesService = new DevicesServiceImplementation(this.devicesApiService); - this.authRequestCryptoService = new AuthRequestCryptoServiceImplementation(this.cryptoService); + this.authRequestService = new AuthRequestService( + this.appIdService, + this.cryptoService, + this.apiService, + this.stateService, + ); this.authService = new AuthService( backgroundMessagingService, @@ -557,7 +562,7 @@ export default class MainBackground { this.passwordStrengthService, this.policyService, this.deviceTrustCryptoService, - this.authRequestCryptoService, + this.authRequestService, ); this.userVerificationApiService = new UserVerificationApiService(this.apiService); diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 64da1579c8..66bb4eef12 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -10,7 +10,10 @@ import { OBSERVABLE_MEMORY_STORAGE, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; -import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; +import { + AuthRequestServiceAbstraction, + LoginStrategyServiceAbstraction, +} from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; @@ -29,7 +32,6 @@ import { import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; @@ -296,8 +298,8 @@ function getBgService(service: keyof MainBackground) { ], }, { - provide: AuthRequestCryptoServiceAbstraction, - useFactory: getBgService("authRequestCryptoService"), + provide: AuthRequestServiceAbstraction, + useFactory: getBgService("authRequestService"), deps: [], }, { diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index fb24d98292..c70be6339e 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -5,6 +5,7 @@ import { program } from "commander"; import * as jsdom from "jsdom"; import { + AuthRequestService, LoginStrategyService, LoginStrategyServiceAbstraction, PinCryptoService, @@ -22,11 +23,9 @@ import { PolicyApiService } from "@bitwarden/common/admin-console/services/polic import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; -import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; @@ -196,7 +195,7 @@ export class Main { sendApiService: SendApiService; devicesApiService: DevicesApiServiceAbstraction; deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; - authRequestCryptoService: AuthRequestCryptoServiceAbstraction; + authRequestService: AuthRequestService; configApiService: ConfigApiServiceAbstraction; configService: CliConfigService; accountService: AccountService; @@ -418,7 +417,12 @@ export class Main { this.platformUtilsService, ); - this.authRequestCryptoService = new AuthRequestCryptoServiceImplementation(this.cryptoService); + this.authRequestService = new AuthRequestService( + this.appIdService, + this.cryptoService, + this.apiService, + this.stateService, + ); this.loginStrategyService = new LoginStrategyService( this.cryptoService, @@ -437,7 +441,7 @@ export class Main { this.passwordStrengthService, this.policyService, this.deviceTrustCryptoService, - this.authRequestCryptoService, + this.authRequestService, ); this.authService = new AuthService( diff --git a/apps/desktop/src/auth/login/login-approval.component.ts b/apps/desktop/src/auth/login/login-approval.component.ts index e39e73662e..048d294f4a 100644 --- a/apps/desktop/src/auth/login/login-approval.component.ts +++ b/apps/desktop/src/auth/login/login-approval.component.ts @@ -4,7 +4,7 @@ import { Component, OnInit, OnDestroy, Inject } from "@angular/core"; import { Subject, firstValueFrom } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; +import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; @@ -46,11 +46,11 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { constructor( @Inject(DIALOG_DATA) private params: LoginApprovalDialogParams, + protected authRequestService: AuthRequestServiceAbstraction, protected stateService: StateService, protected platformUtilsService: PlatformUtilsService, protected i18nService: I18nService, protected apiService: ApiService, - protected loginStrategyService: LoginStrategyServiceAbstraction, protected appIdService: AppIdService, protected cryptoService: CryptoService, private dialogRef: DialogRef, @@ -121,10 +121,9 @@ export class LoginApprovalComponent implements OnInit, OnDestroy { this.i18nService.t("thisRequestIsNoLongerValid"), ); } else { - const loginResponse = await this.loginStrategyService.passwordlessLogin( - this.authRequestResponse.id, - this.authRequestResponse.publicKey, + const loginResponse = await this.authRequestService.approveOrDenyAuthRequest( approve, + this.authRequestResponse, ); this.showResultToast(loginResponse); } diff --git a/apps/desktop/src/auth/login/login-via-auth-request.component.ts b/apps/desktop/src/auth/login/login-via-auth-request.component.ts index 26d6d65496..b4242c36fb 100644 --- a/apps/desktop/src/auth/login/login-via-auth-request.component.ts +++ b/apps/desktop/src/auth/login/login-via-auth-request.component.ts @@ -4,10 +4,12 @@ import { Router } from "@angular/router"; import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component"; import { ModalService } from "@bitwarden/angular/services/modal.service"; -import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; +import { + AuthRequestServiceAbstraction, + LoginStrategyServiceAbstraction, +} from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; @@ -56,7 +58,7 @@ export class LoginViaAuthRequestComponent stateService: StateService, loginService: LoginService, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, - authReqCryptoService: AuthRequestCryptoServiceAbstraction, + authRequestService: AuthRequestServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction, private location: Location, ) { @@ -77,7 +79,7 @@ export class LoginViaAuthRequestComponent stateService, loginService, deviceTrustCryptoService, - authReqCryptoService, + authRequestService, loginStrategyService, ); diff --git a/apps/web/src/app/auth/login/login-via-auth-request.component.ts b/apps/web/src/app/auth/login/login-via-auth-request.component.ts index f745894012..a3bf1160a3 100644 --- a/apps/web/src/app/auth/login/login-via-auth-request.component.ts +++ b/apps/web/src/app/auth/login/login-via-auth-request.component.ts @@ -2,10 +2,12 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component"; -import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common"; +import { + AuthRequestServiceAbstraction, + LoginStrategyServiceAbstraction, +} from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; @@ -46,7 +48,7 @@ export class LoginViaAuthRequestComponent stateService: StateService, loginService: LoginService, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, - authReqCryptoService: AuthRequestCryptoServiceAbstraction, + authRequestService: AuthRequestServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction, ) { super( @@ -66,7 +68,7 @@ export class LoginViaAuthRequestComponent stateService, loginService, deviceTrustCryptoService, - authReqCryptoService, + authRequestService, loginStrategyService, ); } diff --git a/libs/angular/src/auth/components/login-via-auth-request.component.ts b/libs/angular/src/auth/components/login-via-auth-request.component.ts index 666a286457..b9bfe902ea 100644 --- a/libs/angular/src/auth/components/login-via-auth-request.component.ts +++ b/libs/angular/src/auth/components/login-via-auth-request.component.ts @@ -4,11 +4,11 @@ import { Subject, takeUntil } from "rxjs"; import { AuthRequestLoginCredentials, + AuthRequestServiceAbstraction, LoginStrategyServiceAbstraction, } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; @@ -86,7 +86,7 @@ export class LoginViaAuthRequestComponent private stateService: StateService, private loginService: LoginService, private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, - private authReqCryptoService: AuthRequestCryptoServiceAbstraction, + private authRequestService: AuthRequestServiceAbstraction, private loginStrategyService: LoginStrategyServiceAbstraction, ) { super(environmentService, i18nService, platformUtilsService); @@ -367,14 +367,14 @@ export class LoginViaAuthRequestComponent if (adminAuthReqResponse.masterPasswordHash) { // Flow 2: masterPasswordHash is not null // key is authRequestPublicKey(masterKey) + we have authRequestPublicKey(masterPasswordHash) - await this.authReqCryptoService.setKeysAfterDecryptingSharedMasterKeyAndHash( + await this.authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash( adminAuthReqResponse, privateKey, ); } else { // Flow 3: masterPasswordHash is null // we can assume key is authRequestPublicKey(userKey) and we can just decrypt with userKey and proceed to vault - await this.authReqCryptoService.setUserKeyAfterDecryptingSharedUserKey( + await this.authRequestService.setUserKeyAfterDecryptingSharedUserKey( adminAuthReqResponse, privateKey, ); @@ -404,7 +404,7 @@ export class LoginViaAuthRequestComponent // if masterPasswordHash is null, we will always receive key as authRequestPublicKey(userKey) if (response.masterPasswordHash) { const { masterKey, masterKeyHash } = - await this.authReqCryptoService.decryptPubKeyEncryptedMasterKeyAndHash( + await this.authRequestService.decryptPubKeyEncryptedMasterKeyAndHash( response.key, response.masterPasswordHash, this.authRequestKeyPair.privateKey, @@ -419,7 +419,7 @@ export class LoginViaAuthRequestComponent masterKeyHash, ); } else { - const userKey = await this.authReqCryptoService.decryptPubKeyEncryptedUserKey( + const userKey = await this.authRequestService.decryptPubKeyEncryptedUserKey( response.key, this.authRequestKeyPair.privateKey, ); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 6f7716227d..15d98ce787 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1,6 +1,8 @@ import { LOCALE_ID, NgModule } from "@angular/core"; import { + AuthRequestServiceAbstraction, + AuthRequestService, PinCryptoServiceAbstraction, PinCryptoService, LoginStrategyServiceAbstraction, @@ -47,7 +49,6 @@ import { InternalAccountService, } from "@bitwarden/common/auth/abstractions/account.service"; import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; @@ -66,7 +67,6 @@ import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstract import { AccountApiServiceImplementation } from "@bitwarden/common/auth/services/account-api.service"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.service"; -import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; @@ -320,7 +320,7 @@ import { ModalService } from "./modal.service"; PasswordStrengthServiceAbstraction, PolicyServiceAbstraction, DeviceTrustCryptoServiceAbstraction, - AuthRequestCryptoServiceAbstraction, + AuthRequestServiceAbstraction, ], }, { @@ -845,9 +845,14 @@ import { ModalService } from "./modal.service"; ], }, { - provide: AuthRequestCryptoServiceAbstraction, - useClass: AuthRequestCryptoServiceImplementation, - deps: [CryptoServiceAbstraction], + provide: AuthRequestServiceAbstraction, + useClass: AuthRequestService, + deps: [ + AppIdServiceAbstraction, + CryptoServiceAbstraction, + ApiServiceAbstraction, + StateServiceAbstraction, + ], }, { provide: PinCryptoServiceAbstraction, diff --git a/libs/auth/src/common/abstractions/auth-request.service.abstraction.ts b/libs/auth/src/common/abstractions/auth-request.service.abstraction.ts new file mode 100644 index 0000000000..b91444d3e6 --- /dev/null +++ b/libs/auth/src/common/abstractions/auth-request.service.abstraction.ts @@ -0,0 +1,57 @@ +import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; +import { UserKey, MasterKey } from "@bitwarden/common/types/key"; + +export abstract class AuthRequestServiceAbstraction { + /** + * Approve or deny an auth request. + * @param approve True to approve, false to deny. + * @param authRequest The auth request to approve or deny, must have an id and key. + * @returns The updated auth request, the `requestApproved` field will be true if + * approval was successful. + * @throws If the auth request is missing an id or key. + */ + abstract approveOrDenyAuthRequest: ( + approve: boolean, + authRequest: AuthRequestResponse, + ) => Promise; + /** + * Sets the `UserKey` from an auth request. Auth request must have a `UserKey`. + * @param authReqResponse The auth request. + * @param authReqPrivateKey The private key corresponding to the public key sent in the auth request. + */ + abstract setUserKeyAfterDecryptingSharedUserKey: ( + authReqResponse: AuthRequestResponse, + authReqPrivateKey: ArrayBuffer, + ) => Promise; + /** + * Sets the `MasterKey` and `MasterKeyHash` from an auth request. Auth request must have a `MasterKey` and `MasterKeyHash`. + * @param authReqResponse The auth request. + * @param authReqPrivateKey The private key corresponding to the public key sent in the auth request. + */ + abstract setKeysAfterDecryptingSharedMasterKeyAndHash: ( + authReqResponse: AuthRequestResponse, + authReqPrivateKey: ArrayBuffer, + ) => Promise; + /** + * Decrypts a `UserKey` from a public key encrypted `UserKey`. + * @param pubKeyEncryptedUserKey The public key encrypted `UserKey`. + * @param privateKey The private key corresponding to the public key used to encrypt the `UserKey`. + * @returns The decrypted `UserKey`. + */ + abstract decryptPubKeyEncryptedUserKey: ( + pubKeyEncryptedUserKey: string, + privateKey: ArrayBuffer, + ) => Promise; + /** + * Decrypts a `MasterKey` and `MasterKeyHash` from a public key encrypted `MasterKey` and `MasterKeyHash`. + * @param pubKeyEncryptedMasterKey The public key encrypted `MasterKey`. + * @param pubKeyEncryptedMasterKeyHash The public key encrypted `MasterKeyHash`. + * @param privateKey The private key corresponding to the public key used to encrypt the `MasterKey` and `MasterKeyHash`. + * @returns The decrypted `MasterKey` and `MasterKeyHash`. + */ + abstract decryptPubKeyEncryptedMasterKeyAndHash: ( + pubKeyEncryptedMasterKey: string, + pubKeyEncryptedMasterKeyHash: string, + privateKey: ArrayBuffer, + ) => Promise<{ masterKey: MasterKey; masterKeyHash: string }>; +} diff --git a/libs/auth/src/common/abstractions/index.ts b/libs/auth/src/common/abstractions/index.ts index 674a068a74..c4b5bed128 100644 --- a/libs/auth/src/common/abstractions/index.ts +++ b/libs/auth/src/common/abstractions/index.ts @@ -1,2 +1,3 @@ export * from "./pin-crypto.service.abstraction"; export * from "./login-strategy.service"; +export * from "./auth-request.service.abstraction"; diff --git a/libs/auth/src/common/abstractions/login-strategy.service.ts b/libs/auth/src/common/abstractions/login-strategy.service.ts index 25e812eca7..c02337367c 100644 --- a/libs/auth/src/common/abstractions/login-strategy.service.ts +++ b/libs/auth/src/common/abstractions/login-strategy.service.ts @@ -2,7 +2,6 @@ import { Observable } from "rxjs"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; -import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response"; import { MasterKey } from "@bitwarden/common/types/key"; @@ -39,10 +38,5 @@ export abstract class LoginStrategyServiceAbstraction { authingWithPassword: () => boolean; authingWithPasswordless: () => boolean; authResponsePushNotification: (notification: AuthRequestPushNotification) => Promise; - passwordlessLogin: ( - id: string, - key: string, - requestApproved: boolean, - ) => Promise; getPushNotificationObs$: () => Observable; } diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts index 7c0e78922f..99e1445179 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts @@ -1,7 +1,6 @@ import { mock, MockProxy } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; @@ -20,6 +19,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym import { CsprngArray } from "@bitwarden/common/types/csprng"; import { DeviceKey, UserKey, MasterKey } from "@bitwarden/common/types/key"; +import { AuthRequestServiceAbstraction } from "../abstractions"; import { SsoLoginCredentials } from "../models/domain/login-credentials"; import { identityTokenResponseFactory } from "./login.strategy.spec"; @@ -40,7 +40,7 @@ describe("SsoLoginStrategy", () => { let twoFactorService: MockProxy; let keyConnectorService: MockProxy; let deviceTrustCryptoService: MockProxy; - let authRequestCryptoService: MockProxy; + let authRequestService: MockProxy; let i18nService: MockProxy; let ssoLoginStrategy: SsoLoginStrategy; @@ -66,7 +66,7 @@ describe("SsoLoginStrategy", () => { twoFactorService = mock(); keyConnectorService = mock(); deviceTrustCryptoService = mock(); - authRequestCryptoService = mock(); + authRequestService = mock(); i18nService = mock(); tokenService.getTwoFactorToken.mockResolvedValue(null); @@ -85,7 +85,7 @@ describe("SsoLoginStrategy", () => { twoFactorService, keyConnectorService, deviceTrustCryptoService, - authRequestCryptoService, + authRequestService, i18nService, ); credentials = new SsoLoginCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId); 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 6122833d23..4fef55e37f 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.ts @@ -1,5 +1,4 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; @@ -18,6 +17,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { AuthRequestServiceAbstraction } from "../abstractions"; import { SsoLoginCredentials } from "../models/domain/login-credentials"; import { LoginStrategy } from "./login.strategy"; @@ -44,7 +44,7 @@ export class SsoLoginStrategy extends LoginStrategy { twoFactorService: TwoFactorService, private keyConnectorService: KeyConnectorService, private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, - private authReqCryptoService: AuthRequestCryptoServiceAbstraction, + private authRequestService: AuthRequestServiceAbstraction, private i18nService: I18nService, ) { super( @@ -199,14 +199,14 @@ export class SsoLoginStrategy extends LoginStrategy { // if masterPasswordHash has a value, we will always receive authReqResponse.key // as authRequestPublicKey(masterKey) + authRequestPublicKey(masterPasswordHash) if (adminAuthReqResponse.masterPasswordHash) { - await this.authReqCryptoService.setKeysAfterDecryptingSharedMasterKeyAndHash( + await this.authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash( adminAuthReqResponse, adminAuthReqStorable.privateKey, ); } else { // if masterPasswordHash is null, we will always receive authReqResponse.key // as authRequestPublicKey(userKey) - await this.authReqCryptoService.setUserKeyAfterDecryptingSharedUserKey( + await this.authRequestService.setUserKeyAfterDecryptingSharedUserKey( adminAuthReqResponse, adminAuthReqStorable.privateKey, ); diff --git a/libs/common/src/auth/services/auth-request-crypto.service.spec.ts b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts similarity index 56% rename from libs/common/src/auth/services/auth-request-crypto.service.spec.ts rename to libs/auth/src/common/services/auth-request/auth-request.service.spec.ts index 07bed689a3..c6e3449b7a 100644 --- a/libs/common/src/auth/services/auth-request-crypto.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts @@ -1,32 +1,71 @@ import { mock } from "jest-mock-extended"; -import { CryptoService } from "../../platform/abstractions/crypto.service"; -import { Utils } from "../../platform/misc/utils"; -import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; -import { UserKey, MasterKey } from "../../types/key"; -import { AuthRequestCryptoServiceAbstraction } from "../abstractions/auth-request-crypto.service.abstraction"; -import { AuthRequestResponse } from "../models/response/auth-request.response"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; +import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { MasterKey, UserKey } from "@bitwarden/common/types/key"; -import { AuthRequestCryptoServiceImplementation } from "./auth-request-crypto.service.implementation"; +import { AuthRequestService } from "./auth-request.service"; -describe("AuthRequestCryptoService", () => { - let authReqCryptoService: AuthRequestCryptoServiceAbstraction; +describe("AuthRequestService", () => { + let sut: AuthRequestService; + + const appIdService = mock(); const cryptoService = mock(); + const apiService = mock(); + const stateService = mock(); + let mockPrivateKey: Uint8Array; beforeEach(() => { jest.clearAllMocks(); - jest.resetAllMocks(); - authReqCryptoService = new AuthRequestCryptoServiceImplementation(cryptoService); + sut = new AuthRequestService(appIdService, cryptoService, apiService, stateService); mockPrivateKey = new Uint8Array(64); }); - it("instantiates", () => { - expect(authReqCryptoService).not.toBeFalsy(); - }); + describe("approveOrDenyAuthRequest", () => { + beforeEach(() => { + cryptoService.rsaEncrypt.mockResolvedValue({ + encryptedString: "ENCRYPTED_STRING", + } as EncString); + appIdService.getAppId.mockResolvedValue("APP_ID"); + }); + it("should throw if auth request is missing id or key", async () => { + const authRequestNoId = new AuthRequestResponse({ id: "", key: "KEY" }); + const authRequestNoKey = new AuthRequestResponse({ id: "123", key: "" }); + await expect(sut.approveOrDenyAuthRequest(true, authRequestNoId)).rejects.toThrow( + "Auth request has no id", + ); + await expect(sut.approveOrDenyAuthRequest(true, authRequestNoKey)).rejects.toThrow( + "Auth request has no public key", + ); + }); + + it("should use the master key and hash if they exist", async () => { + cryptoService.getMasterKey.mockResolvedValueOnce({ encKey: new Uint8Array(64) } as MasterKey); + stateService.getKeyHash.mockResolvedValueOnce("KEY_HASH"); + + await sut.approveOrDenyAuthRequest(true, new AuthRequestResponse({ id: "123", key: "KEY" })); + + expect(cryptoService.rsaEncrypt).toHaveBeenCalledWith(new Uint8Array(64), expect.anything()); + }); + + it("should use the user key if the master key and hash do not exist", async () => { + cryptoService.getUserKey.mockResolvedValueOnce({ key: new Uint8Array(64) } as UserKey); + + await sut.approveOrDenyAuthRequest(true, new AuthRequestResponse({ id: "123", key: "KEY" })); + + expect(cryptoService.rsaEncrypt).toHaveBeenCalledWith(new Uint8Array(64), expect.anything()); + }); + }); describe("setUserKeyAfterDecryptingSharedUserKey", () => { it("decrypts and sets user key when given valid auth request response and private key", async () => { // Arrange @@ -35,20 +74,15 @@ describe("AuthRequestCryptoService", () => { } as AuthRequestResponse; const mockDecryptedUserKey = {} as UserKey; - jest - .spyOn(authReqCryptoService, "decryptPubKeyEncryptedUserKey") - .mockResolvedValueOnce(mockDecryptedUserKey); + jest.spyOn(sut, "decryptPubKeyEncryptedUserKey").mockResolvedValueOnce(mockDecryptedUserKey); cryptoService.setUserKey.mockResolvedValueOnce(undefined); // Act - await authReqCryptoService.setUserKeyAfterDecryptingSharedUserKey( - mockAuthReqResponse, - mockPrivateKey, - ); + await sut.setUserKeyAfterDecryptingSharedUserKey(mockAuthReqResponse, mockPrivateKey); // Assert - expect(authReqCryptoService.decryptPubKeyEncryptedUserKey).toBeCalledWith( + expect(sut.decryptPubKeyEncryptedUserKey).toBeCalledWith( mockAuthReqResponse.key, mockPrivateKey, ); @@ -68,12 +102,10 @@ describe("AuthRequestCryptoService", () => { const mockDecryptedMasterKeyHash = "mockDecryptedMasterKeyHash"; const mockDecryptedUserKey = {} as UserKey; - jest - .spyOn(authReqCryptoService, "decryptPubKeyEncryptedMasterKeyAndHash") - .mockResolvedValueOnce({ - masterKey: mockDecryptedMasterKey, - masterKeyHash: mockDecryptedMasterKeyHash, - }); + jest.spyOn(sut, "decryptPubKeyEncryptedMasterKeyAndHash").mockResolvedValueOnce({ + masterKey: mockDecryptedMasterKey, + masterKeyHash: mockDecryptedMasterKeyHash, + }); cryptoService.setMasterKey.mockResolvedValueOnce(undefined); cryptoService.setMasterKeyHash.mockResolvedValueOnce(undefined); @@ -81,13 +113,10 @@ describe("AuthRequestCryptoService", () => { cryptoService.setUserKey.mockResolvedValueOnce(undefined); // Act - await authReqCryptoService.setKeysAfterDecryptingSharedMasterKeyAndHash( - mockAuthReqResponse, - mockPrivateKey, - ); + await sut.setKeysAfterDecryptingSharedMasterKeyAndHash(mockAuthReqResponse, mockPrivateKey); // Assert - expect(authReqCryptoService.decryptPubKeyEncryptedMasterKeyAndHash).toBeCalledWith( + expect(sut.decryptPubKeyEncryptedMasterKeyAndHash).toBeCalledWith( mockAuthReqResponse.key, mockAuthReqResponse.masterPasswordHash, mockPrivateKey, @@ -109,7 +138,7 @@ describe("AuthRequestCryptoService", () => { cryptoService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedUserKeyBytes); // Act - const result = await authReqCryptoService.decryptPubKeyEncryptedUserKey( + const result = await sut.decryptPubKeyEncryptedUserKey( mockPubKeyEncryptedUserKey, mockPrivateKey, ); @@ -138,7 +167,7 @@ describe("AuthRequestCryptoService", () => { .mockResolvedValueOnce(mockDecryptedMasterKeyHashBytes); // Act - const result = await authReqCryptoService.decryptPubKeyEncryptedMasterKeyAndHash( + const result = await sut.decryptPubKeyEncryptedMasterKeyAndHash( mockPubKeyEncryptedMasterKey, mockPubKeyEncryptedMasterKeyHash, mockPrivateKey, 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 new file mode 100644 index 0000000000..ad9dd150d3 --- /dev/null +++ b/libs/auth/src/common/services/auth-request/auth-request.service.ts @@ -0,0 +1,129 @@ +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request"; +import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; +import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { MasterKey, UserKey } from "@bitwarden/common/types/key"; + +import { AuthRequestServiceAbstraction } from "../../abstractions/auth-request.service.abstraction"; + +export class AuthRequestService implements AuthRequestServiceAbstraction { + constructor( + private appIdService: AppIdService, + private cryptoService: CryptoService, + private apiService: ApiService, + private stateService: StateService, + ) {} + + async approveOrDenyAuthRequest( + approve: boolean, + authRequest: AuthRequestResponse, + ): Promise { + if (!authRequest.id) { + throw new Error("Auth request has no id"); + } + if (!authRequest.key) { + throw new Error("Auth request has no public key"); + } + const pubKey = Utils.fromB64ToArray(authRequest.key); + + const masterKey = await this.cryptoService.getMasterKey(); + const masterKeyHash = await this.stateService.getKeyHash(); + let encryptedMasterKeyHash; + let keyToEncrypt; + + if (masterKey && masterKeyHash) { + // Only encrypt the master password hash if masterKey exists as + // we won't have a masterKeyHash without a masterKey + encryptedMasterKeyHash = await this.cryptoService.rsaEncrypt( + Utils.fromUtf8ToArray(masterKeyHash), + pubKey, + ); + keyToEncrypt = masterKey.encKey; + } else { + const userKey = await this.cryptoService.getUserKey(); + keyToEncrypt = userKey.key; + } + + const encryptedKey = await this.cryptoService.rsaEncrypt(keyToEncrypt, pubKey); + + const response = new PasswordlessAuthRequest( + encryptedKey.encryptedString, + encryptedMasterKeyHash?.encryptedString, + await this.appIdService.getAppId(), + approve, + ); + return await this.apiService.putAuthRequest(authRequest.id, response); + } + + async setUserKeyAfterDecryptingSharedUserKey( + authReqResponse: AuthRequestResponse, + authReqPrivateKey: Uint8Array, + ) { + const userKey = await this.decryptPubKeyEncryptedUserKey( + authReqResponse.key, + authReqPrivateKey, + ); + await this.cryptoService.setUserKey(userKey); + } + + async setKeysAfterDecryptingSharedMasterKeyAndHash( + authReqResponse: AuthRequestResponse, + authReqPrivateKey: Uint8Array, + ) { + const { masterKey, masterKeyHash } = await this.decryptPubKeyEncryptedMasterKeyAndHash( + authReqResponse.key, + authReqResponse.masterPasswordHash, + authReqPrivateKey, + ); + + // Decrypt and set user key in state + const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey); + + // Set masterKey + masterKeyHash in state after decryption (in case decryption fails) + await this.cryptoService.setMasterKey(masterKey); + await this.cryptoService.setMasterKeyHash(masterKeyHash); + + await this.cryptoService.setUserKey(userKey); + } + + // Decryption helpers + async decryptPubKeyEncryptedUserKey( + pubKeyEncryptedUserKey: string, + privateKey: Uint8Array, + ): Promise { + const decryptedUserKeyBytes = await this.cryptoService.rsaDecrypt( + pubKeyEncryptedUserKey, + privateKey, + ); + + return new SymmetricCryptoKey(decryptedUserKeyBytes) as UserKey; + } + + async decryptPubKeyEncryptedMasterKeyAndHash( + pubKeyEncryptedMasterKey: string, + pubKeyEncryptedMasterKeyHash: string, + privateKey: Uint8Array, + ): Promise<{ masterKey: MasterKey; masterKeyHash: string }> { + const decryptedMasterKeyArrayBuffer = await this.cryptoService.rsaDecrypt( + pubKeyEncryptedMasterKey, + privateKey, + ); + + const decryptedMasterKeyHashArrayBuffer = await this.cryptoService.rsaDecrypt( + pubKeyEncryptedMasterKeyHash, + privateKey, + ); + + const masterKey = new SymmetricCryptoKey(decryptedMasterKeyArrayBuffer) as MasterKey; + const masterKeyHash = Utils.fromBufferToUtf8(decryptedMasterKeyHashArrayBuffer); + + return { + masterKey, + masterKeyHash, + }; + } +} diff --git a/libs/auth/src/common/services/index.ts b/libs/auth/src/common/services/index.ts index 9fb652d40e..2b131c7afc 100644 --- a/libs/auth/src/common/services/index.ts +++ b/libs/auth/src/common/services/index.ts @@ -1,2 +1,3 @@ export * from "./pin-crypto/pin-crypto.service.implementation"; export * from "./login-strategies/login-strategy.service"; +export * from "./auth-request/auth-request.service"; 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 b94c6a150e..f8a51118af 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 @@ -2,7 +2,6 @@ import { Observable, Subject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; @@ -11,8 +10,6 @@ import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication- import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; -import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request"; -import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { PreloginRequest } from "@bitwarden/common/models/request/prelogin.request"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response"; @@ -26,11 +23,10 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { KdfType } from "@bitwarden/common/platform/enums"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { MasterKey } from "@bitwarden/common/types/key"; -import { LoginStrategyServiceAbstraction } from "../../abstractions"; +import { AuthRequestServiceAbstraction, LoginStrategyServiceAbstraction } from "../../abstractions"; import { AuthRequestLoginStrategy } from "../../login-strategies/auth-request-login.strategy"; import { PasswordLoginStrategy } from "../../login-strategies/password-login.strategy"; import { SsoLoginStrategy } from "../../login-strategies/sso-login.strategy"; @@ -110,7 +106,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { protected passwordStrengthService: PasswordStrengthServiceAbstraction, protected policyService: PolicyService, protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, - protected authReqCryptoService: AuthRequestCryptoServiceAbstraction, + protected authRequestService: AuthRequestServiceAbstraction, ) {} async logIn( @@ -160,7 +156,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { this.twoFactorService, this.keyConnectorService, this.deviceTrustCryptoService, - this.authReqCryptoService, + this.authRequestService, this.i18nService, ); break; @@ -290,45 +286,6 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { return this.pushNotificationSubject.asObservable(); } - async passwordlessLogin( - id: string, - key: string, - requestApproved: boolean, - ): Promise { - const pubKey = Utils.fromB64ToArray(key); - - const masterKey = await this.cryptoService.getMasterKey(); - let keyToEncrypt; - let encryptedMasterKeyHash = null; - - if (masterKey) { - keyToEncrypt = masterKey.encKey; - - // Only encrypt the master password hash if masterKey exists as - // we won't have a masterKeyHash without a masterKey - const masterKeyHash = await this.stateService.getKeyHash(); - if (masterKeyHash != null) { - encryptedMasterKeyHash = await this.cryptoService.rsaEncrypt( - Utils.fromUtf8ToArray(masterKeyHash), - pubKey, - ); - } - } else { - const userKey = await this.cryptoService.getUserKey(); - keyToEncrypt = userKey.key; - } - - const encryptedKey = await this.cryptoService.rsaEncrypt(keyToEncrypt, pubKey); - - const request = new PasswordlessAuthRequest( - encryptedKey.encryptedString, - encryptedMasterKeyHash?.encryptedString, - await this.appIdService.getAppId(), - requestApproved, - ); - return await this.apiService.putAuthRequest(id, request); - } - private saveState( strategy: | UserApiLoginStrategy diff --git a/libs/common/src/auth/abstractions/auth-request-crypto.service.abstraction.ts b/libs/common/src/auth/abstractions/auth-request-crypto.service.abstraction.ts deleted file mode 100644 index e81d1a6bb6..0000000000 --- a/libs/common/src/auth/abstractions/auth-request-crypto.service.abstraction.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { UserKey, MasterKey } from "../../types/key"; -import { AuthRequestResponse } from "../models/response/auth-request.response"; - -export abstract class AuthRequestCryptoServiceAbstraction { - setUserKeyAfterDecryptingSharedUserKey: ( - authReqResponse: AuthRequestResponse, - authReqPrivateKey: ArrayBuffer, - ) => Promise; - setKeysAfterDecryptingSharedMasterKeyAndHash: ( - authReqResponse: AuthRequestResponse, - authReqPrivateKey: ArrayBuffer, - ) => Promise; - - decryptPubKeyEncryptedUserKey: ( - pubKeyEncryptedUserKey: string, - privateKey: ArrayBuffer, - ) => Promise; - - decryptPubKeyEncryptedMasterKeyAndHash: ( - pubKeyEncryptedMasterKey: string, - pubKeyEncryptedMasterKeyHash: string, - privateKey: ArrayBuffer, - ) => Promise<{ masterKey: MasterKey; masterKeyHash: string }>; -} diff --git a/libs/common/src/auth/services/auth-request-crypto.service.implementation.ts b/libs/common/src/auth/services/auth-request-crypto.service.implementation.ts deleted file mode 100644 index 446990414c..0000000000 --- a/libs/common/src/auth/services/auth-request-crypto.service.implementation.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { CryptoService } from "../../platform/abstractions/crypto.service"; -import { Utils } from "../../platform/misc/utils"; -import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; -import { UserKey, MasterKey } from "../../types/key"; -import { AuthRequestCryptoServiceAbstraction } from "../abstractions/auth-request-crypto.service.abstraction"; -import { AuthRequestResponse } from "../models/response/auth-request.response"; - -export class AuthRequestCryptoServiceImplementation implements AuthRequestCryptoServiceAbstraction { - constructor(private cryptoService: CryptoService) {} - - async setUserKeyAfterDecryptingSharedUserKey( - authReqResponse: AuthRequestResponse, - authReqPrivateKey: Uint8Array, - ) { - const userKey = await this.decryptPubKeyEncryptedUserKey( - authReqResponse.key, - authReqPrivateKey, - ); - await this.cryptoService.setUserKey(userKey); - } - - async setKeysAfterDecryptingSharedMasterKeyAndHash( - authReqResponse: AuthRequestResponse, - authReqPrivateKey: Uint8Array, - ) { - const { masterKey, masterKeyHash } = await this.decryptPubKeyEncryptedMasterKeyAndHash( - authReqResponse.key, - authReqResponse.masterPasswordHash, - authReqPrivateKey, - ); - - // Decrypt and set user key in state - const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey); - - // Set masterKey + masterKeyHash in state after decryption (in case decryption fails) - await this.cryptoService.setMasterKey(masterKey); - await this.cryptoService.setMasterKeyHash(masterKeyHash); - - await this.cryptoService.setUserKey(userKey); - } - - // Decryption helpers - async decryptPubKeyEncryptedUserKey( - pubKeyEncryptedUserKey: string, - privateKey: Uint8Array, - ): Promise { - const decryptedUserKeyBytes = await this.cryptoService.rsaDecrypt( - pubKeyEncryptedUserKey, - privateKey, - ); - - return new SymmetricCryptoKey(decryptedUserKeyBytes) as UserKey; - } - - async decryptPubKeyEncryptedMasterKeyAndHash( - pubKeyEncryptedMasterKey: string, - pubKeyEncryptedMasterKeyHash: string, - privateKey: Uint8Array, - ): Promise<{ masterKey: MasterKey; masterKeyHash: string }> { - const decryptedMasterKeyArrayBuffer = await this.cryptoService.rsaDecrypt( - pubKeyEncryptedMasterKey, - privateKey, - ); - - const decryptedMasterKeyHashArrayBuffer = await this.cryptoService.rsaDecrypt( - pubKeyEncryptedMasterKeyHash, - privateKey, - ); - - const masterKey = new SymmetricCryptoKey(decryptedMasterKeyArrayBuffer) as MasterKey; - const masterKeyHash = Utils.fromBufferToUtf8(decryptedMasterKeyHashArrayBuffer); - - return { - masterKey, - masterKeyHash, - }; - } -}