mirror of
https://github.com/bitwarden/browser
synced 2025-12-23 11:43:46 +00:00
move auth request notification to service (#8451)
- cleanup hanging promises
This commit is contained in:
@@ -68,7 +68,6 @@ export class LoginViaAuthRequestComponent
|
||||
|
||||
private authRequestKeyPair: { publicKey: Uint8Array; privateKey: Uint8Array };
|
||||
|
||||
// TODO: in future, go to child components and remove child constructors and let deps fall through to the super class
|
||||
constructor(
|
||||
protected router: Router,
|
||||
private cryptoService: CryptoService,
|
||||
@@ -98,14 +97,16 @@ export class LoginViaAuthRequestComponent
|
||||
this.email = this.loginService.getEmail();
|
||||
}
|
||||
|
||||
//gets signalR push notification
|
||||
this.loginStrategyService.authRequestPushNotification$
|
||||
// Gets signalR push notification
|
||||
// Only fires on approval to prevent enumeration
|
||||
this.authRequestService.authRequestPushNotification$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((id) => {
|
||||
// Only fires on approval currently
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.verifyAndHandleApprovedAuthReq(id);
|
||||
this.verifyAndHandleApprovedAuthReq(id).catch((e: Error) => {
|
||||
this.platformUtilsService.showToast("error", this.i18nService.t("error"), e.message);
|
||||
this.logService.error("Failed to use approved auth request: " + e.message);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,10 +165,10 @@ export class LoginViaAuthRequestComponent
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
async ngOnDestroy() {
|
||||
await this.anonymousHubService.stopHubConnection();
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
this.anonymousHubService.stopHubConnection();
|
||||
}
|
||||
|
||||
private async handleExistingAdminAuthRequest(adminAuthReqStorable: AdminAuthRequestStorable) {
|
||||
@@ -213,7 +214,7 @@ export class LoginViaAuthRequestComponent
|
||||
|
||||
// Request still pending response from admin
|
||||
// So, create hub connection so that any approvals will be received via push notification
|
||||
this.anonymousHubService.createHubConnection(adminAuthReqStorable.id);
|
||||
await this.anonymousHubService.createHubConnection(adminAuthReqStorable.id);
|
||||
}
|
||||
|
||||
private async handleExistingAdminAuthReqDeletedOrDenied() {
|
||||
@@ -273,7 +274,7 @@ export class LoginViaAuthRequestComponent
|
||||
}
|
||||
|
||||
if (reqResponse.id) {
|
||||
this.anonymousHubService.createHubConnection(reqResponse.id);
|
||||
await this.anonymousHubService.createHubConnection(reqResponse.id);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
|
||||
@@ -866,7 +866,7 @@ const safeProviders: SafeProvider[] = [
|
||||
safeProvider({
|
||||
provide: AnonymousHubServiceAbstraction,
|
||||
useClass: AnonymousHubService,
|
||||
deps: [EnvironmentService, LoginStrategyServiceAbstraction, LogService],
|
||||
deps: [EnvironmentService, AuthRequestServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: ValidationServiceAbstraction,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
|
||||
import { UserKey, MasterKey } from "@bitwarden/common/types/key";
|
||||
|
||||
export abstract class AuthRequestServiceAbstraction {
|
||||
/** Emits an auth request id when an auth request has been approved. */
|
||||
authRequestPushNotification$: Observable<string>;
|
||||
/**
|
||||
* Approve or deny an auth request.
|
||||
* @param approve True to approve, false to deny.
|
||||
@@ -54,4 +59,11 @@ export abstract class AuthRequestServiceAbstraction {
|
||||
pubKeyEncryptedMasterKeyHash: string,
|
||||
privateKey: ArrayBuffer,
|
||||
) => Promise<{ masterKey: MasterKey; masterKeyHash: string }>;
|
||||
|
||||
/**
|
||||
* Handles incoming auth request push notifications.
|
||||
* @param notification push notification.
|
||||
* @remark We should only be receiving approved push notifications to prevent enumeration.
|
||||
*/
|
||||
abstract sendAuthRequestPushNotification: (notification: AuthRequestPushNotification) => void;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-
|
||||
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";
|
||||
|
||||
import {
|
||||
@@ -21,10 +20,6 @@ export abstract class LoginStrategyServiceAbstraction {
|
||||
* Emits null if the session has timed out.
|
||||
*/
|
||||
currentAuthType$: Observable<AuthenticationType | null>;
|
||||
/**
|
||||
* Emits when an auth request has been approved.
|
||||
*/
|
||||
authRequestPushNotification$: Observable<string>;
|
||||
/**
|
||||
* If the login strategy uses the email address of the user, this
|
||||
* will return it. Otherwise, it will return null.
|
||||
@@ -77,10 +72,6 @@ export abstract class LoginStrategyServiceAbstraction {
|
||||
* Creates a master key from the provided master password and email.
|
||||
*/
|
||||
makePreloginKey: (masterPassword: string, email: string) => Promise<MasterKey>;
|
||||
/**
|
||||
* Sends a notification to {@link LoginStrategyServiceAbstraction.authRequestPushNotification}
|
||||
*/
|
||||
sendAuthRequestPushNotification: (notification: AuthRequestPushNotification) => Promise<void>;
|
||||
/**
|
||||
* Sends a response to an auth request.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Observable, Subject } from "rxjs";
|
||||
|
||||
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 { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.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";
|
||||
@@ -11,6 +14,9 @@ import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
||||
import { AuthRequestServiceAbstraction } from "../../abstractions/auth-request.service.abstraction";
|
||||
|
||||
export class AuthRequestService implements AuthRequestServiceAbstraction {
|
||||
private authRequestPushNotificationSubject = new Subject<string>();
|
||||
authRequestPushNotification$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private appIdService: AppIdService,
|
||||
private cryptoService: CryptoService,
|
||||
@@ -126,4 +132,10 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
|
||||
masterKeyHash,
|
||||
};
|
||||
}
|
||||
|
||||
sendAuthRequestPushNotification(notification: AuthRequestPushNotification): void {
|
||||
if (notification.id != null) {
|
||||
this.authRequestPushNotificationSubject.next(notification.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
combineLatestWith,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
firstValueFrom,
|
||||
map,
|
||||
Observable,
|
||||
@@ -23,7 +22,6 @@ import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
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";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
@@ -81,8 +79,6 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||
>;
|
||||
|
||||
currentAuthType$: Observable<AuthenticationType | null>;
|
||||
// TODO: move to auth request service
|
||||
authRequestPushNotification$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
protected cryptoService: CryptoService,
|
||||
@@ -114,9 +110,6 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||
);
|
||||
|
||||
this.currentAuthType$ = this.currentAuthnTypeState.state$;
|
||||
this.authRequestPushNotification$ = this.authRequestPushNotificationState.state$.pipe(
|
||||
filter((id) => id != null),
|
||||
);
|
||||
this.loginStrategy$ = this.currentAuthnTypeState.state$.pipe(
|
||||
distinctUntilChanged(),
|
||||
combineLatestWith(this.loginStrategyCacheState.state$),
|
||||
@@ -256,13 +249,6 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||
return await this.cryptoService.makeMasterKey(masterPassword, email, kdf, kdfConfig);
|
||||
}
|
||||
|
||||
// TODO move to auth request service
|
||||
async sendAuthRequestPushNotification(notification: AuthRequestPushNotification): Promise<void> {
|
||||
if (notification.id != null) {
|
||||
await this.authRequestPushNotificationState.update((_) => notification.id);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to auth request service
|
||||
async passwordlessLogin(
|
||||
id: string,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export abstract class AnonymousHubService {
|
||||
createHubConnection: (token: string) => void;
|
||||
stopHubConnection: () => void;
|
||||
createHubConnection: (token: string) => Promise<void>;
|
||||
stopHubConnection: () => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ import {
|
||||
import { MessagePackHubProtocol } from "@microsoft/signalr-protocol-msgpack";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LoginStrategyServiceAbstraction } from "../../../../auth/src/common/abstractions/login-strategy.service";
|
||||
import { AuthRequestServiceAbstraction } from "../../../../auth/src/common/abstractions";
|
||||
import { NotificationType } from "../../enums";
|
||||
import {
|
||||
AuthRequestPushNotification,
|
||||
NotificationResponse,
|
||||
} from "../../models/response/notification.response";
|
||||
import { EnvironmentService } from "../../platform/abstractions/environment.service";
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { AnonymousHubService as AnonymousHubServiceAbstraction } from "../abstractions/anonymous-hub.service";
|
||||
|
||||
export class AnonymousHubService implements AnonymousHubServiceAbstraction {
|
||||
@@ -22,8 +22,7 @@ export class AnonymousHubService implements AnonymousHubServiceAbstraction {
|
||||
|
||||
constructor(
|
||||
private environmentService: EnvironmentService,
|
||||
private loginStrategyService: LoginStrategyServiceAbstraction,
|
||||
private logService: LogService,
|
||||
private authRequestService: AuthRequestServiceAbstraction,
|
||||
) {}
|
||||
|
||||
async createHubConnection(token: string) {
|
||||
@@ -37,26 +36,25 @@ export class AnonymousHubService implements AnonymousHubServiceAbstraction {
|
||||
.withHubProtocol(new MessagePackHubProtocol() as IHubProtocol)
|
||||
.build();
|
||||
|
||||
this.anonHubConnection.start().catch((error) => this.logService.error(error));
|
||||
await this.anonHubConnection.start();
|
||||
|
||||
this.anonHubConnection.on("AuthRequestResponseRecieved", (data: any) => {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.ProcessNotification(new NotificationResponse(data));
|
||||
});
|
||||
}
|
||||
|
||||
stopHubConnection() {
|
||||
async stopHubConnection() {
|
||||
if (this.anonHubConnection) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.anonHubConnection.stop();
|
||||
await this.anonHubConnection.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private async ProcessNotification(notification: NotificationResponse) {
|
||||
await this.loginStrategyService.sendAuthRequestPushNotification(
|
||||
notification.payload as AuthRequestPushNotification,
|
||||
);
|
||||
private ProcessNotification(notification: NotificationResponse) {
|
||||
switch (notification.type) {
|
||||
case NotificationType.AuthRequestResponse:
|
||||
this.authRequestService.sendAuthRequestPushNotification(
|
||||
notification.payload as AuthRequestPushNotification,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user