1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 13:23:34 +00:00

[PM-5499] Create Auth Request Service (#8056)

* create auth request service

* copy methods from auth crypto service

* register new auth request service

* remove refs to auth request crypto service

* remove auth request crypto service

* remove passwordless login method from login strategy service

* add docs to auth request service
This commit is contained in:
Jake Fink
2024-02-26 10:07:08 -05:00
committed by GitHub
parent d02651583f
commit 1435203e12
23 changed files with 381 additions and 274 deletions

View File

@@ -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<AuthRequestCryptoServiceAbstraction> {
return factory(
cache,
"authRequestCryptoService",
opts,
async () => new AuthRequestCryptoServiceImplementation(await cryptoServiceFactory(cache, opts)),
);
}

View File

@@ -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<AuthRequestServiceAbstraction> {
return factory(
cache,
"authRequestService",
opts,
async () =>
new AuthRequestService(
await appIdServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await apiServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
),
);
}

View File

@@ -52,9 +52,9 @@ import {
} from "../../../tools/background/service_factories/password-strength-service.factory"; } from "../../../tools/background/service_factories/password-strength-service.factory";
import { import {
authRequestCryptoServiceFactory, authRequestServiceFactory,
AuthRequestCryptoServiceInitOptions, AuthRequestServiceInitOptions,
} from "./auth-request-crypto-service.factory"; } from "./auth-request-service.factory";
import { import {
deviceTrustCryptoServiceFactory, deviceTrustCryptoServiceFactory,
DeviceTrustCryptoServiceInitOptions, DeviceTrustCryptoServiceInitOptions,
@@ -84,7 +84,7 @@ export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions
PolicyServiceInitOptions & PolicyServiceInitOptions &
PasswordStrengthServiceInitOptions & PasswordStrengthServiceInitOptions &
DeviceTrustCryptoServiceInitOptions & DeviceTrustCryptoServiceInitOptions &
AuthRequestCryptoServiceInitOptions; AuthRequestServiceInitOptions;
export function loginStrategyServiceFactory( export function loginStrategyServiceFactory(
cache: { loginStrategyService?: LoginStrategyServiceAbstraction } & CachedServices, cache: { loginStrategyService?: LoginStrategyServiceAbstraction } & CachedServices,
@@ -112,7 +112,7 @@ export function loginStrategyServiceFactory(
await passwordStrengthServiceFactory(cache, opts), await passwordStrengthServiceFactory(cache, opts),
await policyServiceFactory(cache, opts), await policyServiceFactory(cache, opts),
await deviceTrustCryptoServiceFactory(cache, opts), await deviceTrustCryptoServiceFactory(cache, opts),
await authRequestCryptoServiceFactory(cache, opts), await authRequestServiceFactory(cache, opts),
), ),
); );
} }

View File

@@ -3,10 +3,12 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
@@ -48,7 +50,7 @@ export class LoginViaAuthRequestComponent
loginService: LoginService, loginService: LoginService,
syncService: SyncService, syncService: SyncService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
authReqCryptoService: AuthRequestCryptoServiceAbstraction, authRequestService: AuthRequestServiceAbstraction,
loginStrategyService: LoginStrategyServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction,
private location: Location, private location: Location,
) { ) {
@@ -69,7 +71,7 @@ export class LoginViaAuthRequestComponent
stateService, stateService,
loginService, loginService,
deviceTrustCryptoService, deviceTrustCryptoService,
authReqCryptoService, authRequestService,
loginStrategyService, loginStrategyService,
); );
super.onSuccessfulLogin = async () => { super.onSuccessfulLogin = async () => {

View File

@@ -5,6 +5,8 @@ import {
PinCryptoService, PinCryptoService,
LoginStrategyServiceAbstraction, LoginStrategyServiceAbstraction,
LoginStrategyService, LoginStrategyService,
AuthRequestServiceAbstraction,
AuthRequestService,
} from "@bitwarden/auth/common"; } from "@bitwarden/auth/common";
import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service"; import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service";
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.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 { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.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 { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; 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 { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
@@ -274,7 +274,7 @@ export default class MainBackground {
devicesApiService: DevicesApiServiceAbstraction; devicesApiService: DevicesApiServiceAbstraction;
devicesService: DevicesServiceAbstraction; devicesService: DevicesServiceAbstraction;
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction;
authRequestCryptoService: AuthRequestCryptoServiceAbstraction; authRequestService: AuthRequestServiceAbstraction;
accountService: AccountServiceAbstraction; accountService: AccountServiceAbstraction;
globalStateProvider: GlobalStateProvider; globalStateProvider: GlobalStateProvider;
pinCryptoService: PinCryptoServiceAbstraction; pinCryptoService: PinCryptoServiceAbstraction;
@@ -531,7 +531,12 @@ export default class MainBackground {
this.devicesService = new DevicesServiceImplementation(this.devicesApiService); 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( this.authService = new AuthService(
backgroundMessagingService, backgroundMessagingService,
@@ -557,7 +562,7 @@ export default class MainBackground {
this.passwordStrengthService, this.passwordStrengthService,
this.policyService, this.policyService,
this.deviceTrustCryptoService, this.deviceTrustCryptoService,
this.authRequestCryptoService, this.authRequestService,
); );
this.userVerificationApiService = new UserVerificationApiService(this.apiService); this.userVerificationApiService = new UserVerificationApiService(this.apiService);

View File

@@ -10,7 +10,10 @@ import {
OBSERVABLE_MEMORY_STORAGE, OBSERVABLE_MEMORY_STORAGE,
} from "@bitwarden/angular/services/injection-tokens"; } from "@bitwarden/angular/services/injection-tokens";
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.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 { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.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 { 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 { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
@@ -296,8 +298,8 @@ function getBgService<T>(service: keyof MainBackground) {
], ],
}, },
{ {
provide: AuthRequestCryptoServiceAbstraction, provide: AuthRequestServiceAbstraction,
useFactory: getBgService<AuthRequestCryptoServiceAbstraction>("authRequestCryptoService"), useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
deps: [], deps: [],
}, },
{ {

View File

@@ -5,6 +5,7 @@ import { program } from "commander";
import * as jsdom from "jsdom"; import * as jsdom from "jsdom";
import { import {
AuthRequestService,
LoginStrategyService, LoginStrategyService,
LoginStrategyServiceAbstraction, LoginStrategyServiceAbstraction,
PinCryptoService, 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 { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; 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 { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
@@ -196,7 +195,7 @@ export class Main {
sendApiService: SendApiService; sendApiService: SendApiService;
devicesApiService: DevicesApiServiceAbstraction; devicesApiService: DevicesApiServiceAbstraction;
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction;
authRequestCryptoService: AuthRequestCryptoServiceAbstraction; authRequestService: AuthRequestService;
configApiService: ConfigApiServiceAbstraction; configApiService: ConfigApiServiceAbstraction;
configService: CliConfigService; configService: CliConfigService;
accountService: AccountService; accountService: AccountService;
@@ -418,7 +417,12 @@ export class Main {
this.platformUtilsService, 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.loginStrategyService = new LoginStrategyService(
this.cryptoService, this.cryptoService,
@@ -437,7 +441,7 @@ export class Main {
this.passwordStrengthService, this.passwordStrengthService,
this.policyService, this.policyService,
this.deviceTrustCryptoService, this.deviceTrustCryptoService,
this.authRequestCryptoService, this.authRequestService,
); );
this.authService = new AuthService( this.authService = new AuthService(

View File

@@ -4,7 +4,7 @@ import { Component, OnInit, OnDestroy, Inject } from "@angular/core";
import { Subject, firstValueFrom } from "rxjs"; import { Subject, firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
@@ -46,11 +46,11 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
constructor( constructor(
@Inject(DIALOG_DATA) private params: LoginApprovalDialogParams, @Inject(DIALOG_DATA) private params: LoginApprovalDialogParams,
protected authRequestService: AuthRequestServiceAbstraction,
protected stateService: StateService, protected stateService: StateService,
protected platformUtilsService: PlatformUtilsService, protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService, protected i18nService: I18nService,
protected apiService: ApiService, protected apiService: ApiService,
protected loginStrategyService: LoginStrategyServiceAbstraction,
protected appIdService: AppIdService, protected appIdService: AppIdService,
protected cryptoService: CryptoService, protected cryptoService: CryptoService,
private dialogRef: DialogRef, private dialogRef: DialogRef,
@@ -121,10 +121,9 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
this.i18nService.t("thisRequestIsNoLongerValid"), this.i18nService.t("thisRequestIsNoLongerValid"),
); );
} else { } else {
const loginResponse = await this.loginStrategyService.passwordlessLogin( const loginResponse = await this.authRequestService.approveOrDenyAuthRequest(
this.authRequestResponse.id,
this.authRequestResponse.publicKey,
approve, approve,
this.authRequestResponse,
); );
this.showResultToast(loginResponse); this.showResultToast(loginResponse);
} }

View File

@@ -4,10 +4,12 @@ import { Router } from "@angular/router";
import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component"; import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component";
import { ModalService } from "@bitwarden/angular/services/modal.service"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
@@ -56,7 +58,7 @@ export class LoginViaAuthRequestComponent
stateService: StateService, stateService: StateService,
loginService: LoginService, loginService: LoginService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
authReqCryptoService: AuthRequestCryptoServiceAbstraction, authRequestService: AuthRequestServiceAbstraction,
loginStrategyService: LoginStrategyServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction,
private location: Location, private location: Location,
) { ) {
@@ -77,7 +79,7 @@ export class LoginViaAuthRequestComponent
stateService, stateService,
loginService, loginService,
deviceTrustCryptoService, deviceTrustCryptoService,
authReqCryptoService, authRequestService,
loginStrategyService, loginStrategyService,
); );

View File

@@ -2,10 +2,12 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
@@ -46,7 +48,7 @@ export class LoginViaAuthRequestComponent
stateService: StateService, stateService: StateService,
loginService: LoginService, loginService: LoginService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
authReqCryptoService: AuthRequestCryptoServiceAbstraction, authRequestService: AuthRequestServiceAbstraction,
loginStrategyService: LoginStrategyServiceAbstraction, loginStrategyService: LoginStrategyServiceAbstraction,
) { ) {
super( super(
@@ -66,7 +68,7 @@ export class LoginViaAuthRequestComponent
stateService, stateService,
loginService, loginService,
deviceTrustCryptoService, deviceTrustCryptoService,
authReqCryptoService, authRequestService,
loginStrategyService, loginStrategyService,
); );
} }

View File

@@ -4,11 +4,11 @@ import { Subject, takeUntil } from "rxjs";
import { import {
AuthRequestLoginCredentials, AuthRequestLoginCredentials,
AuthRequestServiceAbstraction,
LoginStrategyServiceAbstraction, LoginStrategyServiceAbstraction,
} from "@bitwarden/auth/common"; } from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
@@ -86,7 +86,7 @@ export class LoginViaAuthRequestComponent
private stateService: StateService, private stateService: StateService,
private loginService: LoginService, private loginService: LoginService,
private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
private authReqCryptoService: AuthRequestCryptoServiceAbstraction, private authRequestService: AuthRequestServiceAbstraction,
private loginStrategyService: LoginStrategyServiceAbstraction, private loginStrategyService: LoginStrategyServiceAbstraction,
) { ) {
super(environmentService, i18nService, platformUtilsService); super(environmentService, i18nService, platformUtilsService);
@@ -367,14 +367,14 @@ export class LoginViaAuthRequestComponent
if (adminAuthReqResponse.masterPasswordHash) { if (adminAuthReqResponse.masterPasswordHash) {
// Flow 2: masterPasswordHash is not null // Flow 2: masterPasswordHash is not null
// key is authRequestPublicKey(masterKey) + we have authRequestPublicKey(masterPasswordHash) // key is authRequestPublicKey(masterKey) + we have authRequestPublicKey(masterPasswordHash)
await this.authReqCryptoService.setKeysAfterDecryptingSharedMasterKeyAndHash( await this.authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash(
adminAuthReqResponse, adminAuthReqResponse,
privateKey, privateKey,
); );
} else { } else {
// Flow 3: masterPasswordHash is null // Flow 3: masterPasswordHash is null
// we can assume key is authRequestPublicKey(userKey) and we can just decrypt with userKey and proceed to vault // 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, adminAuthReqResponse,
privateKey, privateKey,
); );
@@ -404,7 +404,7 @@ export class LoginViaAuthRequestComponent
// if masterPasswordHash is null, we will always receive key as authRequestPublicKey(userKey) // if masterPasswordHash is null, we will always receive key as authRequestPublicKey(userKey)
if (response.masterPasswordHash) { if (response.masterPasswordHash) {
const { masterKey, masterKeyHash } = const { masterKey, masterKeyHash } =
await this.authReqCryptoService.decryptPubKeyEncryptedMasterKeyAndHash( await this.authRequestService.decryptPubKeyEncryptedMasterKeyAndHash(
response.key, response.key,
response.masterPasswordHash, response.masterPasswordHash,
this.authRequestKeyPair.privateKey, this.authRequestKeyPair.privateKey,
@@ -419,7 +419,7 @@ export class LoginViaAuthRequestComponent
masterKeyHash, masterKeyHash,
); );
} else { } else {
const userKey = await this.authReqCryptoService.decryptPubKeyEncryptedUserKey( const userKey = await this.authRequestService.decryptPubKeyEncryptedUserKey(
response.key, response.key,
this.authRequestKeyPair.privateKey, this.authRequestKeyPair.privateKey,
); );

View File

@@ -1,6 +1,8 @@
import { LOCALE_ID, NgModule } from "@angular/core"; import { LOCALE_ID, NgModule } from "@angular/core";
import { import {
AuthRequestServiceAbstraction,
AuthRequestService,
PinCryptoServiceAbstraction, PinCryptoServiceAbstraction,
PinCryptoService, PinCryptoService,
LoginStrategyServiceAbstraction, LoginStrategyServiceAbstraction,
@@ -47,7 +49,6 @@ import {
InternalAccountService, InternalAccountService,
} from "@bitwarden/common/auth/abstractions/account.service"; } from "@bitwarden/common/auth/abstractions/account.service";
import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/auth/abstractions/anonymous-hub.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 { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.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 { AccountApiServiceImplementation } from "@bitwarden/common/auth/services/account-api.service";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.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 { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
@@ -320,7 +320,7 @@ import { ModalService } from "./modal.service";
PasswordStrengthServiceAbstraction, PasswordStrengthServiceAbstraction,
PolicyServiceAbstraction, PolicyServiceAbstraction,
DeviceTrustCryptoServiceAbstraction, DeviceTrustCryptoServiceAbstraction,
AuthRequestCryptoServiceAbstraction, AuthRequestServiceAbstraction,
], ],
}, },
{ {
@@ -845,9 +845,14 @@ import { ModalService } from "./modal.service";
], ],
}, },
{ {
provide: AuthRequestCryptoServiceAbstraction, provide: AuthRequestServiceAbstraction,
useClass: AuthRequestCryptoServiceImplementation, useClass: AuthRequestService,
deps: [CryptoServiceAbstraction], deps: [
AppIdServiceAbstraction,
CryptoServiceAbstraction,
ApiServiceAbstraction,
StateServiceAbstraction,
],
}, },
{ {
provide: PinCryptoServiceAbstraction, provide: PinCryptoServiceAbstraction,

View File

@@ -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<AuthRequestResponse>;
/**
* 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<void>;
/**
* 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<void>;
/**
* 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<UserKey>;
/**
* 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 }>;
}

View File

@@ -1,2 +1,3 @@
export * from "./pin-crypto.service.abstraction"; export * from "./pin-crypto.service.abstraction";
export * from "./login-strategy.service"; export * from "./login-strategy.service";
export * from "./auth-request.service.abstraction";

View File

@@ -2,7 +2,6 @@ import { Observable } from "rxjs";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; 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 { 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 { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
import { MasterKey } from "@bitwarden/common/types/key"; import { MasterKey } from "@bitwarden/common/types/key";
@@ -39,10 +38,5 @@ export abstract class LoginStrategyServiceAbstraction {
authingWithPassword: () => boolean; authingWithPassword: () => boolean;
authingWithPasswordless: () => boolean; authingWithPasswordless: () => boolean;
authResponsePushNotification: (notification: AuthRequestPushNotification) => Promise<any>; authResponsePushNotification: (notification: AuthRequestPushNotification) => Promise<any>;
passwordlessLogin: (
id: string,
key: string,
requestApproved: boolean,
) => Promise<AuthRequestResponse>;
getPushNotificationObs$: () => Observable<any>; getPushNotificationObs$: () => Observable<any>;
} }

View File

@@ -1,7 +1,6 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.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 { CsprngArray } from "@bitwarden/common/types/csprng";
import { DeviceKey, UserKey, MasterKey } from "@bitwarden/common/types/key"; import { DeviceKey, UserKey, MasterKey } from "@bitwarden/common/types/key";
import { AuthRequestServiceAbstraction } from "../abstractions";
import { SsoLoginCredentials } from "../models/domain/login-credentials"; import { SsoLoginCredentials } from "../models/domain/login-credentials";
import { identityTokenResponseFactory } from "./login.strategy.spec"; import { identityTokenResponseFactory } from "./login.strategy.spec";
@@ -40,7 +40,7 @@ describe("SsoLoginStrategy", () => {
let twoFactorService: MockProxy<TwoFactorService>; let twoFactorService: MockProxy<TwoFactorService>;
let keyConnectorService: MockProxy<KeyConnectorService>; let keyConnectorService: MockProxy<KeyConnectorService>;
let deviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>; let deviceTrustCryptoService: MockProxy<DeviceTrustCryptoServiceAbstraction>;
let authRequestCryptoService: MockProxy<AuthRequestCryptoServiceAbstraction>; let authRequestService: MockProxy<AuthRequestServiceAbstraction>;
let i18nService: MockProxy<I18nService>; let i18nService: MockProxy<I18nService>;
let ssoLoginStrategy: SsoLoginStrategy; let ssoLoginStrategy: SsoLoginStrategy;
@@ -66,7 +66,7 @@ describe("SsoLoginStrategy", () => {
twoFactorService = mock<TwoFactorService>(); twoFactorService = mock<TwoFactorService>();
keyConnectorService = mock<KeyConnectorService>(); keyConnectorService = mock<KeyConnectorService>();
deviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>(); deviceTrustCryptoService = mock<DeviceTrustCryptoServiceAbstraction>();
authRequestCryptoService = mock<AuthRequestCryptoServiceAbstraction>(); authRequestService = mock<AuthRequestServiceAbstraction>();
i18nService = mock<I18nService>(); i18nService = mock<I18nService>();
tokenService.getTwoFactorToken.mockResolvedValue(null); tokenService.getTwoFactorToken.mockResolvedValue(null);
@@ -85,7 +85,7 @@ describe("SsoLoginStrategy", () => {
twoFactorService, twoFactorService,
keyConnectorService, keyConnectorService,
deviceTrustCryptoService, deviceTrustCryptoService,
authRequestCryptoService, authRequestService,
i18nService, i18nService,
); );
credentials = new SsoLoginCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId); credentials = new SsoLoginCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId);

View File

@@ -1,5 +1,4 @@
import { ApiService } from "@bitwarden/common/abstractions/api.service"; 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { AuthRequestServiceAbstraction } from "../abstractions";
import { SsoLoginCredentials } from "../models/domain/login-credentials"; import { SsoLoginCredentials } from "../models/domain/login-credentials";
import { LoginStrategy } from "./login.strategy"; import { LoginStrategy } from "./login.strategy";
@@ -44,7 +44,7 @@ export class SsoLoginStrategy extends LoginStrategy {
twoFactorService: TwoFactorService, twoFactorService: TwoFactorService,
private keyConnectorService: KeyConnectorService, private keyConnectorService: KeyConnectorService,
private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, private deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
private authReqCryptoService: AuthRequestCryptoServiceAbstraction, private authRequestService: AuthRequestServiceAbstraction,
private i18nService: I18nService, private i18nService: I18nService,
) { ) {
super( super(
@@ -199,14 +199,14 @@ export class SsoLoginStrategy extends LoginStrategy {
// if masterPasswordHash has a value, we will always receive authReqResponse.key // if masterPasswordHash has a value, we will always receive authReqResponse.key
// as authRequestPublicKey(masterKey) + authRequestPublicKey(masterPasswordHash) // as authRequestPublicKey(masterKey) + authRequestPublicKey(masterPasswordHash)
if (adminAuthReqResponse.masterPasswordHash) { if (adminAuthReqResponse.masterPasswordHash) {
await this.authReqCryptoService.setKeysAfterDecryptingSharedMasterKeyAndHash( await this.authRequestService.setKeysAfterDecryptingSharedMasterKeyAndHash(
adminAuthReqResponse, adminAuthReqResponse,
adminAuthReqStorable.privateKey, adminAuthReqStorable.privateKey,
); );
} else { } else {
// if masterPasswordHash is null, we will always receive authReqResponse.key // if masterPasswordHash is null, we will always receive authReqResponse.key
// as authRequestPublicKey(userKey) // as authRequestPublicKey(userKey)
await this.authReqCryptoService.setUserKeyAfterDecryptingSharedUserKey( await this.authRequestService.setUserKeyAfterDecryptingSharedUserKey(
adminAuthReqResponse, adminAuthReqResponse,
adminAuthReqStorable.privateKey, adminAuthReqStorable.privateKey,
); );

View File

@@ -1,32 +1,71 @@
import { mock } from "jest-mock-extended"; import { mock } from "jest-mock-extended";
import { CryptoService } from "../../platform/abstractions/crypto.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { Utils } from "../../platform/misc/utils"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { UserKey, MasterKey } from "../../types/key"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { AuthRequestCryptoServiceAbstraction } from "../abstractions/auth-request-crypto.service.abstraction"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { AuthRequestResponse } from "../models/response/auth-request.response"; 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", () => { describe("AuthRequestService", () => {
let authReqCryptoService: AuthRequestCryptoServiceAbstraction; let sut: AuthRequestService;
const appIdService = mock<AppIdService>();
const cryptoService = mock<CryptoService>(); const cryptoService = mock<CryptoService>();
const apiService = mock<ApiService>();
const stateService = mock<StateService>();
let mockPrivateKey: Uint8Array; let mockPrivateKey: Uint8Array;
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
jest.resetAllMocks();
authReqCryptoService = new AuthRequestCryptoServiceImplementation(cryptoService); sut = new AuthRequestService(appIdService, cryptoService, apiService, stateService);
mockPrivateKey = new Uint8Array(64); mockPrivateKey = new Uint8Array(64);
}); });
it("instantiates", () => { describe("approveOrDenyAuthRequest", () => {
expect(authReqCryptoService).not.toBeFalsy(); 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", () => { describe("setUserKeyAfterDecryptingSharedUserKey", () => {
it("decrypts and sets user key when given valid auth request response and private key", async () => { it("decrypts and sets user key when given valid auth request response and private key", async () => {
// Arrange // Arrange
@@ -35,20 +74,15 @@ describe("AuthRequestCryptoService", () => {
} as AuthRequestResponse; } as AuthRequestResponse;
const mockDecryptedUserKey = {} as UserKey; const mockDecryptedUserKey = {} as UserKey;
jest jest.spyOn(sut, "decryptPubKeyEncryptedUserKey").mockResolvedValueOnce(mockDecryptedUserKey);
.spyOn(authReqCryptoService, "decryptPubKeyEncryptedUserKey")
.mockResolvedValueOnce(mockDecryptedUserKey);
cryptoService.setUserKey.mockResolvedValueOnce(undefined); cryptoService.setUserKey.mockResolvedValueOnce(undefined);
// Act // Act
await authReqCryptoService.setUserKeyAfterDecryptingSharedUserKey( await sut.setUserKeyAfterDecryptingSharedUserKey(mockAuthReqResponse, mockPrivateKey);
mockAuthReqResponse,
mockPrivateKey,
);
// Assert // Assert
expect(authReqCryptoService.decryptPubKeyEncryptedUserKey).toBeCalledWith( expect(sut.decryptPubKeyEncryptedUserKey).toBeCalledWith(
mockAuthReqResponse.key, mockAuthReqResponse.key,
mockPrivateKey, mockPrivateKey,
); );
@@ -68,9 +102,7 @@ describe("AuthRequestCryptoService", () => {
const mockDecryptedMasterKeyHash = "mockDecryptedMasterKeyHash"; const mockDecryptedMasterKeyHash = "mockDecryptedMasterKeyHash";
const mockDecryptedUserKey = {} as UserKey; const mockDecryptedUserKey = {} as UserKey;
jest jest.spyOn(sut, "decryptPubKeyEncryptedMasterKeyAndHash").mockResolvedValueOnce({
.spyOn(authReqCryptoService, "decryptPubKeyEncryptedMasterKeyAndHash")
.mockResolvedValueOnce({
masterKey: mockDecryptedMasterKey, masterKey: mockDecryptedMasterKey,
masterKeyHash: mockDecryptedMasterKeyHash, masterKeyHash: mockDecryptedMasterKeyHash,
}); });
@@ -81,13 +113,10 @@ describe("AuthRequestCryptoService", () => {
cryptoService.setUserKey.mockResolvedValueOnce(undefined); cryptoService.setUserKey.mockResolvedValueOnce(undefined);
// Act // Act
await authReqCryptoService.setKeysAfterDecryptingSharedMasterKeyAndHash( await sut.setKeysAfterDecryptingSharedMasterKeyAndHash(mockAuthReqResponse, mockPrivateKey);
mockAuthReqResponse,
mockPrivateKey,
);
// Assert // Assert
expect(authReqCryptoService.decryptPubKeyEncryptedMasterKeyAndHash).toBeCalledWith( expect(sut.decryptPubKeyEncryptedMasterKeyAndHash).toBeCalledWith(
mockAuthReqResponse.key, mockAuthReqResponse.key,
mockAuthReqResponse.masterPasswordHash, mockAuthReqResponse.masterPasswordHash,
mockPrivateKey, mockPrivateKey,
@@ -109,7 +138,7 @@ describe("AuthRequestCryptoService", () => {
cryptoService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedUserKeyBytes); cryptoService.rsaDecrypt.mockResolvedValueOnce(mockDecryptedUserKeyBytes);
// Act // Act
const result = await authReqCryptoService.decryptPubKeyEncryptedUserKey( const result = await sut.decryptPubKeyEncryptedUserKey(
mockPubKeyEncryptedUserKey, mockPubKeyEncryptedUserKey,
mockPrivateKey, mockPrivateKey,
); );
@@ -138,7 +167,7 @@ describe("AuthRequestCryptoService", () => {
.mockResolvedValueOnce(mockDecryptedMasterKeyHashBytes); .mockResolvedValueOnce(mockDecryptedMasterKeyHashBytes);
// Act // Act
const result = await authReqCryptoService.decryptPubKeyEncryptedMasterKeyAndHash( const result = await sut.decryptPubKeyEncryptedMasterKeyAndHash(
mockPubKeyEncryptedMasterKey, mockPubKeyEncryptedMasterKey,
mockPubKeyEncryptedMasterKeyHash, mockPubKeyEncryptedMasterKeyHash,
mockPrivateKey, mockPrivateKey,

View File

@@ -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<AuthRequestResponse> {
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<UserKey> {
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,
};
}
}

View File

@@ -1,2 +1,3 @@
export * from "./pin-crypto/pin-crypto.service.implementation"; export * from "./pin-crypto/pin-crypto.service.implementation";
export * from "./login-strategies/login-strategy.service"; export * from "./login-strategies/login-strategy.service";
export * from "./auth-request/auth-request.service";

View File

@@ -2,7 +2,6 @@ import { Observable, Subject } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; 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 { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.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 { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; 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 { 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 { PreloginRequest } from "@bitwarden/common/models/request/prelogin.request";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { KdfType } from "@bitwarden/common/platform/enums"; import { KdfType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { MasterKey } from "@bitwarden/common/types/key"; 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 { AuthRequestLoginStrategy } from "../../login-strategies/auth-request-login.strategy";
import { PasswordLoginStrategy } from "../../login-strategies/password-login.strategy"; import { PasswordLoginStrategy } from "../../login-strategies/password-login.strategy";
import { SsoLoginStrategy } from "../../login-strategies/sso-login.strategy"; import { SsoLoginStrategy } from "../../login-strategies/sso-login.strategy";
@@ -110,7 +106,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
protected passwordStrengthService: PasswordStrengthServiceAbstraction, protected passwordStrengthService: PasswordStrengthServiceAbstraction,
protected policyService: PolicyService, protected policyService: PolicyService,
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
protected authReqCryptoService: AuthRequestCryptoServiceAbstraction, protected authRequestService: AuthRequestServiceAbstraction,
) {} ) {}
async logIn( async logIn(
@@ -160,7 +156,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
this.twoFactorService, this.twoFactorService,
this.keyConnectorService, this.keyConnectorService,
this.deviceTrustCryptoService, this.deviceTrustCryptoService,
this.authReqCryptoService, this.authRequestService,
this.i18nService, this.i18nService,
); );
break; break;
@@ -290,45 +286,6 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
return this.pushNotificationSubject.asObservable(); return this.pushNotificationSubject.asObservable();
} }
async passwordlessLogin(
id: string,
key: string,
requestApproved: boolean,
): Promise<AuthRequestResponse> {
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( private saveState(
strategy: strategy:
| UserApiLoginStrategy | UserApiLoginStrategy

View File

@@ -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<void>;
setKeysAfterDecryptingSharedMasterKeyAndHash: (
authReqResponse: AuthRequestResponse,
authReqPrivateKey: ArrayBuffer,
) => Promise<void>;
decryptPubKeyEncryptedUserKey: (
pubKeyEncryptedUserKey: string,
privateKey: ArrayBuffer,
) => Promise<UserKey>;
decryptPubKeyEncryptedMasterKeyAndHash: (
pubKeyEncryptedMasterKey: string,
pubKeyEncryptedMasterKeyHash: string,
privateKey: ArrayBuffer,
) => Promise<{ masterKey: MasterKey; masterKeyHash: string }>;
}

View File

@@ -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<UserKey> {
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,
};
}
}