1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

Moved methods

This commit is contained in:
Todd Martin
2025-03-18 16:07:37 -04:00
parent e8c4c570e9
commit 82903fdab7
13 changed files with 87 additions and 59 deletions

View File

@@ -16,7 +16,7 @@ import { filter, first, map, take } from "rxjs/operators";
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthRequestApiService } from "@bitwarden/auth/common";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
@@ -110,7 +110,7 @@ export class VaultComponent implements OnInit, OnDestroy {
private totpService: TotpService,
private passwordRepromptService: PasswordRepromptService,
private searchBarService: SearchBarService,
private apiService: ApiService,
private authRequestApiService: AuthRequestApiService,
private dialogService: DialogService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
private toastService: ToastService,
@@ -231,7 +231,7 @@ export class VaultComponent implements OnInit, OnDestroy {
this.searchBarService.setEnabled(true);
this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault"));
const authRequest = await this.apiService.getLastAuthRequest();
const authRequest = await this.authRequestApiService.getLastAuthRequest();
if (authRequest != null) {
this.messagingService.send("openLoginApproval", {
notificationId: authRequest.id,

View File

@@ -472,6 +472,7 @@ const safeProviders: SafeProvider[] = [
VaultTimeoutSettingsService,
KdfConfigService,
TaskSchedulerService,
AuthRequestApiService,
],
}),
safeProvider({
@@ -1157,7 +1158,7 @@ const safeProviders: SafeProvider[] = [
InternalMasterPasswordServiceAbstraction,
KeyService,
EncryptService,
ApiServiceAbstraction,
AuthRequestApiService,
StateProvider,
],
}),

View File

@@ -7,10 +7,10 @@ import { Subject, firstValueFrom, map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
AuthRequestApiService,
AuthRequestServiceAbstraction,
LoginApprovalComponentServiceAbstraction as LoginApprovalComponentService,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
@@ -59,7 +59,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
protected accountService: AccountService,
protected platformUtilsService: PlatformUtilsService,
protected i18nService: I18nService,
protected apiService: ApiService,
protected apiService: AuthRequestApiService,
protected appIdService: AppIdService,
protected keyService: KeyService,
private dialogRef: DialogRef,

View File

@@ -1,3 +1,4 @@
import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request";
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
@@ -34,4 +35,23 @@ export abstract class AuthRequestApiService {
* @returns A promise that resolves to the auth request response.
*/
abstract postAuthRequest: (request: AuthRequest) => Promise<AuthRequestResponse>;
/**
* Updates an auth request by its ID, which is used to approve or deny the request.
*
* @param id The ID of the auth request.
* @param request The auth request update, indicating whether the request is approved or denied.
* @returns A promise that resolves to the auth request response.
*/
abstract putAuthRequest: (
id: string,
request: AuthRequestUpdateRequest,
) => Promise<AuthRequestResponse>;
/**
* Gets the most-recently-created auth request for the logged-in user.
*
* @returns A promise that resolves to the auth request response.
*/
abstract getLastAuthRequest: () => Promise<AuthRequestResponse>;
}

View File

@@ -14,7 +14,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response"
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { UserId } from "@bitwarden/common/types/guid";
import { AuthRequestServiceAbstraction } from "../abstractions";
import { AuthRequestApiService, AuthRequestServiceAbstraction } from "../abstractions";
import { SsoLoginCredentials } from "../models/domain/login-credentials";
import { CacheData } from "../services/login-strategies/login-strategy.state";
@@ -72,6 +72,7 @@ export class SsoLoginStrategy extends LoginStrategy {
private deviceTrustService: DeviceTrustServiceAbstraction,
private authRequestService: AuthRequestServiceAbstraction,
private i18nService: I18nService,
private authRequestApiService: AuthRequestApiService,
...sharedDeps: ConstructorParameters<typeof LoginStrategy>
) {
super(...sharedDeps);
@@ -230,7 +231,9 @@ export class SsoLoginStrategy extends LoginStrategy {
let adminAuthReqResponse: AuthRequestResponse;
try {
adminAuthReqResponse = await this.apiService.getAuthRequest(adminAuthReqStorable.id);
adminAuthReqResponse = await this.authRequestApiService.getAuthRequest(
adminAuthReqStorable.id,
);
} catch (error) {
if (error instanceof ErrorResponse && error.statusCode === HttpStatusCode.NotFound) {
// if we get a 404, it means the auth request has been deleted so clear it from storage

View File

@@ -1,6 +1,8 @@
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request";
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { AuthRequestApiService } from "../../abstractions/auth-request-api.service";
@@ -75,4 +77,30 @@ export class DefaultAuthRequestApiService implements AuthRequestApiService {
throw e;
}
}
async putAuthRequest(
id: string,
request: AuthRequestUpdateRequest,
): Promise<AuthRequestResponse> {
const path = `/auth-requests/${id}`;
const r = await this.apiService.send("PUT", path, request, true, true);
return new AuthRequestResponse(r);
}
async getAuthRequests(): Promise<ListResponse<AuthRequestResponse>> {
const path = `/auth-requests/`;
const r = await this.apiService.send("GET", path, null, true, true);
return new ListResponse(r, AuthRequestResponse);
}
async getLastAuthRequest(): Promise<AuthRequestResponse> {
const requests = await this.getAuthRequests();
const activeRequests = requests.data.filter(
(m: AuthRequestResponse) => !m.isAnswered && !m.isExpired,
);
const lastRequest = activeRequests.sort((a: AuthRequestResponse, b: AuthRequestResponse) =>
a.creationDate.localeCompare(b.creationDate),
)[activeRequests.length - 1];
return lastRequest;
}
}

View File

@@ -1,6 +1,5 @@
import { mock } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service";
@@ -15,6 +14,8 @@ import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { AuthRequestApiService } from "../../abstractions";
import { AuthRequestService } from "./auth-request.service";
describe("AuthRequestService", () => {
@@ -26,7 +27,7 @@ describe("AuthRequestService", () => {
const appIdService = mock<AppIdService>();
const keyService = mock<KeyService>();
const encryptService = mock<EncryptService>();
const apiService = mock<ApiService>();
const apiService = mock<AuthRequestApiService>();
let mockPrivateKey: Uint8Array;
let mockPublicKey: Uint8Array;

View File

@@ -3,10 +3,9 @@
import { Observable, Subject, firstValueFrom } from "rxjs";
import { Jsonify } from "type-fest";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request";
import { AuthRequestUpdateRequest } from "@bitwarden/common/auth/models/request/auth-request-update.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
@@ -24,6 +23,7 @@ import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { KeyService } from "@bitwarden/key-management";
import { AuthRequestApiService } from "../../abstractions";
import { AuthRequestServiceAbstraction } from "../../abstractions/auth-request.service.abstraction";
/**
@@ -53,7 +53,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
private keyService: KeyService,
private encryptService: EncryptService,
private apiService: ApiService,
private apiService: AuthRequestApiService,
private stateProvider: StateProvider,
) {
this.authRequestPushNotification$ = this.authRequestPushNotificationSubject.asObservable();
@@ -124,7 +124,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
const encryptedKey = await this.encryptService.rsaEncrypt(keyToEncrypt, pubKey);
const response = new PasswordlessAuthRequest(
const response = new AuthRequestUpdateRequest(
encryptedKey.encryptedString,
encryptedMasterKeyHash?.encryptedString,
await this.appIdService.getAppId(),

View File

@@ -45,7 +45,11 @@ import {
KdfConfigService,
} from "@bitwarden/key-management";
import { AuthRequestServiceAbstraction, LoginStrategyServiceAbstraction } from "../../abstractions";
import {
AuthRequestApiService,
AuthRequestServiceAbstraction,
LoginStrategyServiceAbstraction,
} from "../../abstractions";
import { InternalUserDecryptionOptionsServiceAbstraction } from "../../abstractions/user-decryption-options.service.abstraction";
import {
AuthRequestLoginStrategy,
@@ -131,6 +135,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
protected vaultTimeoutSettingsService: VaultTimeoutSettingsService,
protected kdfConfigService: KdfConfigService,
protected taskSchedulerService: TaskSchedulerService,
protected authRequestApiService: AuthRequestApiService,
) {
this.currentAuthnTypeState = this.stateProvider.get(CURRENT_LOGIN_STRATEGY_KEY);
this.loginStrategyCacheState = this.stateProvider.get(CACHE_KEY);
@@ -426,6 +431,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
this.deviceTrustService,
this.authRequestService,
this.i18nService,
this.authRequestApiService,
...sharedDeps,
);
case AuthenticationType.UserApiKey:

View File

@@ -47,7 +47,7 @@ import { SsoTokenRequest } from "../auth/models/request/identity-token/sso-token
import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-api-token.request";
import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request";
import { PasswordHintRequest } from "../auth/models/request/password-hint.request";
import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request";
import { AuthRequestUpdateRequest } from "../auth/models/request/auth-request-update.request";
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
@@ -183,11 +183,6 @@ export abstract class ApiService {
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
postConvertToKeyConnector: () => Promise<void>;
//passwordless
getAuthRequest: (id: string) => Promise<AuthRequestResponse>;
putAuthRequest: (id: string, request: PasswordlessAuthRequest) => Promise<AuthRequestResponse>;
getAuthRequests: () => Promise<ListResponse<AuthRequestResponse>>;
getLastAuthRequest: () => Promise<AuthRequestResponse>;
getUserBillingHistory: () => Promise<BillingHistoryResponse>;
getUserBillingPayment: () => Promise<BillingPaymentResponse>;

View File

@@ -0,0 +1,12 @@
/**
* Represents a request to update an AuthRequest with either approval or denial of the request.
* If the request is approved, the update will contain the key and/or hash to be shared with the requesting device.
*/
export class AuthRequestUpdateRequest {
constructor(
readonly key: string,
readonly masterPasswordHash: string,
readonly deviceIdentifier: string,
readonly requestApproved: boolean,
) {}
}

View File

@@ -1,8 +0,0 @@
export class PasswordlessAuthRequest {
constructor(
readonly key: string,
readonly masterPasswordHash: string,
readonly deviceIdentifier: string,
readonly requestApproved: boolean,
) {}
}

View File

@@ -55,7 +55,6 @@ import { TokenTwoFactorRequest } from "../auth/models/request/identity-token/tok
import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-api-token.request";
import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request";
import { PasswordHintRequest } from "../auth/models/request/password-hint.request";
import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request";
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
@@ -68,7 +67,6 @@ import { UpdateTwoFactorWebAuthnDeleteRequest } from "../auth/models/request/upd
import { UpdateTwoFactorWebAuthnRequest } from "../auth/models/request/update-two-factor-web-authn.request";
import { UpdateTwoFactorYubikeyOtpRequest } from "../auth/models/request/update-two-factor-yubikey-otp.request";
import { ApiKeyResponse } from "../auth/models/response/api-key.response";
import { AuthRequestResponse } from "../auth/models/response/auth-request.response";
import { DeviceVerificationResponse } from "../auth/models/response/device-verification.response";
import { IdentityCaptchaResponse } from "../auth/models/response/identity-captcha.response";
import { IdentityDeviceVerificationResponse } from "../auth/models/response/identity-device-verification.response";
@@ -273,34 +271,6 @@ export class ApiService implements ApiServiceAbstraction {
}
}
// TODO: PM-3519: Create and move to AuthRequest Api service
async getAuthRequest(id: string): Promise<AuthRequestResponse> {
const path = `/auth-requests/${id}`;
const r = await this.send("GET", path, null, true, true);
return new AuthRequestResponse(r);
}
async putAuthRequest(id: string, request: PasswordlessAuthRequest): Promise<AuthRequestResponse> {
const path = `/auth-requests/${id}`;
const r = await this.send("PUT", path, request, true, true);
return new AuthRequestResponse(r);
}
async getAuthRequests(): Promise<ListResponse<AuthRequestResponse>> {
const path = `/auth-requests/`;
const r = await this.send("GET", path, null, true, true);
return new ListResponse(r, AuthRequestResponse);
}
async getLastAuthRequest(): Promise<AuthRequestResponse> {
const requests = await this.getAuthRequests();
const activeRequests = requests.data.filter((m) => !m.isAnswered && !m.isExpired);
const lastRequest = activeRequests.sort((a: AuthRequestResponse, b: AuthRequestResponse) =>
a.creationDate.localeCompare(b.creationDate),
)[activeRequests.length - 1];
return lastRequest;
}
// Account APIs
async getProfile(): Promise<ProfileResponse> {