mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
Auth/PM-16947 - Web - Device Management - Add Manage Auth Requests support (#12809)
* PM-16947 - JsLibServices - register default DefaultLoginApprovalComponentService * PM-16947 - DeviceResponse - add interface for DevicePendingAuthRequest * PM-16947 - Web translations - migrate all LoginApprovalComponent translations from desktop to web * PM-16947 - LoginApprovalComp - (1) Add loading state (2) Refactor to return proper boolean results (3) Don't create race condition by trying to respond to the close event in the dialog and re-sending responses upon approve or deny click * PM-16947 - DeviceManagementComponent - added support for approving and denying auth requests. * PM-16947 - LoginApprovalComp - Add validation error * PM-16947 - LoginApprovalComponent - remove validation service for now. * PM-16947 - Re add validation * PM-16947 - Fix LoginApprovalComponent tests
This commit is contained in:
@@ -48,17 +48,31 @@
|
|||||||
<i [class]="getDeviceIcon(row.type)" class="bwi-lg" aria-hidden="true"></i>
|
<i [class]="getDeviceIcon(row.type)" class="bwi-lg" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<ng-container *ngIf="row.hasPendingAuthRequest">
|
||||||
|
<a bitLink href="#" appStopClick (click)="managePendingAuthRequest(row)">
|
||||||
{{ row.displayName }}
|
{{ row.displayName }}
|
||||||
<span *ngIf="row.trusted" class="tw-text-sm tw-text-muted tw-block">
|
</a>
|
||||||
|
|
||||||
|
<span class="tw-text-sm tw-text-muted tw-block">
|
||||||
|
{{ "needsApproval" | i18n }}
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!row.hasPendingAuthRequest">
|
||||||
|
{{ row.displayName }}
|
||||||
|
<span
|
||||||
|
*ngIf="row.trusted && !row.hasPendingAuthRequest"
|
||||||
|
class="tw-text-sm tw-text-muted tw-block"
|
||||||
|
>
|
||||||
{{ "trusted" | i18n }}
|
{{ "trusted" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td bitCell>
|
<td bitCell>
|
||||||
<span *ngIf="isCurrentDevice(row)" bitBadge variant="primary">{{
|
<span *ngIf="isCurrentDevice(row)" bitBadge variant="primary">{{
|
||||||
"currentSession" | i18n
|
"currentSession" | i18n
|
||||||
}}</span>
|
}}</span>
|
||||||
<span *ngIf="hasPendingAuthRequest(row)" bitBadge variant="warning">{{
|
<span *ngIf="row.hasPendingAuthRequest" bitBadge variant="warning">{{
|
||||||
"requestPending" | i18n
|
"requestPending" | i18n
|
||||||
}}</span>
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { combineLatest, firstValueFrom } from "rxjs";
|
||||||
import { switchMap } from "rxjs/operators";
|
|
||||||
|
|
||||||
|
import { LoginApprovalComponent } from "@bitwarden/auth/angular";
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
||||||
|
import {
|
||||||
|
DevicePendingAuthRequest,
|
||||||
|
DeviceResponse,
|
||||||
|
} from "@bitwarden/common/auth/abstractions/devices/responses/device.response";
|
||||||
import { DeviceView } from "@bitwarden/common/auth/abstractions/devices/views/device.view";
|
import { DeviceView } from "@bitwarden/common/auth/abstractions/devices/views/device.view";
|
||||||
import { DeviceType, DeviceTypeMetadata } from "@bitwarden/common/enums";
|
import { DeviceType, DeviceTypeMetadata } from "@bitwarden/common/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -26,7 +30,8 @@ interface DeviceTableData {
|
|||||||
loginStatus: string;
|
loginStatus: string;
|
||||||
firstLogin: Date;
|
firstLogin: Date;
|
||||||
trusted: boolean;
|
trusted: boolean;
|
||||||
devicePendingAuthRequest: object | null;
|
devicePendingAuthRequest: DevicePendingAuthRequest | null;
|
||||||
|
hasPendingAuthRequest: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,28 +57,25 @@ export class DeviceManagementComponent {
|
|||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
) {
|
) {
|
||||||
this.devicesService
|
combineLatest([this.devicesService.getCurrentDevice$(), this.devicesService.getDevices$()])
|
||||||
.getCurrentDevice$()
|
.pipe(takeUntilDestroyed())
|
||||||
.pipe(
|
|
||||||
takeUntilDestroyed(),
|
|
||||||
switchMap((currentDevice) => {
|
|
||||||
this.currentDevice = new DeviceView(currentDevice);
|
|
||||||
return this.devicesService.getDevices$();
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (devices) => {
|
next: ([currentDevice, devices]: [DeviceResponse, Array<DeviceView>]) => {
|
||||||
this.dataSource.data = devices.map((device) => {
|
this.currentDevice = new DeviceView(currentDevice);
|
||||||
|
|
||||||
|
this.dataSource.data = devices.map((device: DeviceView): DeviceTableData => {
|
||||||
return {
|
return {
|
||||||
id: device.id,
|
id: device.id,
|
||||||
type: device.type,
|
type: device.type,
|
||||||
displayName: this.getHumanReadableDeviceType(device.type),
|
displayName: this.getHumanReadableDeviceType(device.type),
|
||||||
loginStatus: this.getLoginStatus(device),
|
loginStatus: this.getLoginStatus(device),
|
||||||
devicePendingAuthRequest: device.response.devicePendingAuthRequest,
|
|
||||||
firstLogin: new Date(device.creationDate),
|
firstLogin: new Date(device.creationDate),
|
||||||
trusted: device.response.isTrusted,
|
trusted: device.response.isTrusted,
|
||||||
|
devicePendingAuthRequest: device.response.devicePendingAuthRequest,
|
||||||
|
hasPendingAuthRequest: this.hasPendingAuthRequest(device.response),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
@@ -176,15 +178,36 @@ export class DeviceManagementComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a device has a pending auth request
|
* Check if a device has a pending auth request
|
||||||
* @param device - The device
|
* @param device - The device response
|
||||||
* @returns True if the device has a pending auth request, false otherwise
|
* @returns True if the device has a pending auth request, false otherwise
|
||||||
*/
|
*/
|
||||||
protected hasPendingAuthRequest(device: DeviceTableData): boolean {
|
private hasPendingAuthRequest(device: DeviceResponse): boolean {
|
||||||
return (
|
return (
|
||||||
device.devicePendingAuthRequest !== undefined && device.devicePendingAuthRequest !== null
|
device.devicePendingAuthRequest !== undefined && device.devicePendingAuthRequest !== null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a dialog to approve or deny a pending auth request for a device
|
||||||
|
*/
|
||||||
|
async managePendingAuthRequest(device: DeviceTableData) {
|
||||||
|
if (device.devicePendingAuthRequest === undefined || device.devicePendingAuthRequest === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogRef = LoginApprovalComponent.open(this.dialogService, {
|
||||||
|
notificationId: device.devicePendingAuthRequest.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await firstValueFrom(dialogRef.closed);
|
||||||
|
|
||||||
|
if (result !== undefined && typeof result === "boolean") {
|
||||||
|
// auth request approved or denied so reset
|
||||||
|
device.devicePendingAuthRequest = null;
|
||||||
|
device.hasPendingAuthRequest = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a device
|
* Remove a device
|
||||||
* @param device - The device
|
* @param device - The device
|
||||||
|
|||||||
@@ -3813,6 +3813,67 @@
|
|||||||
"trusted": {
|
"trusted": {
|
||||||
"message": "Trusted"
|
"message": "Trusted"
|
||||||
},
|
},
|
||||||
|
"needsApproval": {
|
||||||
|
"message": "Needs approval"
|
||||||
|
},
|
||||||
|
"areYouTryingtoLogin": {
|
||||||
|
"message": "Are you trying to log in?"
|
||||||
|
},
|
||||||
|
"logInAttemptBy": {
|
||||||
|
"message": "Login attempt by $EMAIL$",
|
||||||
|
"placeholders": {
|
||||||
|
"email": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "name@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deviceType": {
|
||||||
|
"message": "Device Type"
|
||||||
|
},
|
||||||
|
"ipAddress": {
|
||||||
|
"message": "IP Address"
|
||||||
|
},
|
||||||
|
"confirmLogIn": {
|
||||||
|
"message": "Confirm login"
|
||||||
|
},
|
||||||
|
"denyLogIn": {
|
||||||
|
"message": "Deny login"
|
||||||
|
},
|
||||||
|
"thisRequestIsNoLongerValid": {
|
||||||
|
"message": "This request is no longer valid."
|
||||||
|
},
|
||||||
|
"logInConfirmedForEmailOnDevice": {
|
||||||
|
"message": "Login confirmed for $EMAIL$ on $DEVICE$",
|
||||||
|
"placeholders": {
|
||||||
|
"email": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "name@example.com"
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"content": "$2",
|
||||||
|
"example": "iOS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"youDeniedALogInAttemptFromAnotherDevice": {
|
||||||
|
"message": "You denied a login attempt from another device. If this really was you, try to log in with the device again."
|
||||||
|
},
|
||||||
|
"loginRequestHasAlreadyExpired": {
|
||||||
|
"message": "Login request has already expired."
|
||||||
|
},
|
||||||
|
"justNow": {
|
||||||
|
"message": "Just now"
|
||||||
|
},
|
||||||
|
"requestedXMinutesAgo": {
|
||||||
|
"message": "Requested $MINUTES$ minutes ago",
|
||||||
|
"placeholders": {
|
||||||
|
"minutes": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"creatingAccountOn": {
|
"creatingAccountOn": {
|
||||||
"message": "Creating account on"
|
"message": "Creating account on"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
DefaultLoginComponentService,
|
DefaultLoginComponentService,
|
||||||
LoginDecryptionOptionsService,
|
LoginDecryptionOptionsService,
|
||||||
DefaultLoginDecryptionOptionsService,
|
DefaultLoginDecryptionOptionsService,
|
||||||
|
DefaultLoginApprovalComponentService,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import {
|
import {
|
||||||
AuthRequestServiceAbstraction,
|
AuthRequestServiceAbstraction,
|
||||||
@@ -39,6 +40,7 @@ import {
|
|||||||
DefaultAuthRequestApiService,
|
DefaultAuthRequestApiService,
|
||||||
DefaultLoginSuccessHandlerService,
|
DefaultLoginSuccessHandlerService,
|
||||||
LoginSuccessHandlerService,
|
LoginSuccessHandlerService,
|
||||||
|
LoginApprovalComponentServiceAbstraction,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||||
@@ -1405,6 +1407,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: DefaultAuthRequestApiService,
|
useClass: DefaultAuthRequestApiService,
|
||||||
deps: [ApiServiceAbstraction, LogService],
|
deps: [ApiServiceAbstraction, LogService],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: LoginApprovalComponentServiceAbstraction,
|
||||||
|
useClass: DefaultLoginApprovalComponentService,
|
||||||
|
deps: [],
|
||||||
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: LoginDecryptionOptionsService,
|
provide: LoginDecryptionOptionsService,
|
||||||
useClass: DefaultLoginDecryptionOptionsService,
|
useClass: DefaultLoginDecryptionOptionsService,
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
<bit-dialog>
|
<bit-dialog>
|
||||||
<span bitDialogTitle>{{ "areYouTryingtoLogin" | i18n }}</span>
|
<span bitDialogTitle>{{ "areYouTryingtoLogin" | i18n }}</span>
|
||||||
<ng-container bitDialogContent>
|
<ng-container bitDialogContent>
|
||||||
|
<ng-container *ngIf="loading">
|
||||||
|
<div class="tw-flex tw-items-center tw-justify-center" *ngIf="loading">
|
||||||
|
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!loading">
|
||||||
<h4 class="tw-mb-3">{{ "logInAttemptBy" | i18n: email }}</h4>
|
<h4 class="tw-mb-3">{{ "logInAttemptBy" | i18n: email }}</h4>
|
||||||
<div>
|
<div>
|
||||||
<b>{{ "fingerprintPhraseHeader" | i18n }}</b>
|
<b>{{ "fingerprintPhraseHeader" | i18n }}</b>
|
||||||
@@ -19,13 +26,14 @@
|
|||||||
<p>{{ requestTimeText }}</p>
|
<p>{{ requestTimeText }}</p>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
<ng-container bitDialogFooter>
|
<ng-container bitDialogFooter>
|
||||||
<button
|
<button
|
||||||
bitButton
|
bitButton
|
||||||
type="button"
|
type="button"
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
[bitAction]="approveLogin"
|
[bitAction]="approveLogin"
|
||||||
[bitDialogClose]="true"
|
[disabled]="loading"
|
||||||
>
|
>
|
||||||
{{ "confirmLogIn" | i18n }}
|
{{ "confirmLogIn" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
@@ -34,7 +42,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
buttonType="secondary"
|
buttonType="secondary"
|
||||||
[bitAction]="denyLogin"
|
[bitAction]="denyLogin"
|
||||||
[bitDialogClose]="true"
|
[disabled]="loading"
|
||||||
>
|
>
|
||||||
{{ "denyLogIn" | i18n }}
|
{{ "denyLogIn" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth
|
|||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { ToastService } from "@bitwarden/components";
|
import { ToastService } from "@bitwarden/components";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
@@ -29,6 +30,7 @@ describe("LoginApprovalComponent", () => {
|
|||||||
let i18nService: MockProxy<I18nService>;
|
let i18nService: MockProxy<I18nService>;
|
||||||
let dialogRef: MockProxy<DialogRef>;
|
let dialogRef: MockProxy<DialogRef>;
|
||||||
let toastService: MockProxy<ToastService>;
|
let toastService: MockProxy<ToastService>;
|
||||||
|
let validationService: MockProxy<ValidationService>;
|
||||||
|
|
||||||
const testNotificationId = "test-notification-id";
|
const testNotificationId = "test-notification-id";
|
||||||
const testEmail = "test@bitwarden.com";
|
const testEmail = "test@bitwarden.com";
|
||||||
@@ -41,6 +43,7 @@ describe("LoginApprovalComponent", () => {
|
|||||||
i18nService = mock<I18nService>();
|
i18nService = mock<I18nService>();
|
||||||
dialogRef = mock<DialogRef>();
|
dialogRef = mock<DialogRef>();
|
||||||
toastService = mock<ToastService>();
|
toastService = mock<ToastService>();
|
||||||
|
validationService = mock<ValidationService>();
|
||||||
|
|
||||||
accountService.activeAccount$ = of({
|
accountService.activeAccount$ = of({
|
||||||
email: testEmail,
|
email: testEmail,
|
||||||
@@ -62,6 +65,7 @@ describe("LoginApprovalComponent", () => {
|
|||||||
{ provide: KeyService, useValue: mock<KeyService>() },
|
{ provide: KeyService, useValue: mock<KeyService>() },
|
||||||
{ provide: DialogRef, useValue: dialogRef },
|
{ provide: DialogRef, useValue: dialogRef },
|
||||||
{ provide: ToastService, useValue: toastService },
|
{ provide: ToastService, useValue: toastService },
|
||||||
|
{ provide: ValidationService, useValue: validationService },
|
||||||
{
|
{
|
||||||
provide: LoginApprovalComponentServiceAbstraction,
|
provide: LoginApprovalComponentServiceAbstraction,
|
||||||
useValue: mock<LoginApprovalComponentServiceAbstraction>(),
|
useValue: mock<LoginApprovalComponentServiceAbstraction>(),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth
|
|||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import {
|
import {
|
||||||
AsyncActionsModule,
|
AsyncActionsModule,
|
||||||
@@ -40,6 +41,8 @@ export interface LoginApprovalDialogParams {
|
|||||||
imports: [CommonModule, AsyncActionsModule, ButtonModule, DialogModule, JslibModule],
|
imports: [CommonModule, AsyncActionsModule, ButtonModule, DialogModule, JslibModule],
|
||||||
})
|
})
|
||||||
export class LoginApprovalComponent implements OnInit, OnDestroy {
|
export class LoginApprovalComponent implements OnInit, OnDestroy {
|
||||||
|
loading = true;
|
||||||
|
|
||||||
notificationId: string;
|
notificationId: string;
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
@@ -62,25 +65,25 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
|
|||||||
private dialogRef: DialogRef,
|
private dialogRef: DialogRef,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private loginApprovalComponentService: LoginApprovalComponentService,
|
private loginApprovalComponentService: LoginApprovalComponentService,
|
||||||
|
private validationService: ValidationService,
|
||||||
) {
|
) {
|
||||||
this.notificationId = params.notificationId;
|
this.notificationId = params.notificationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnDestroy(): Promise<void> {
|
async ngOnDestroy(): Promise<void> {
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
const closedWithButton = await firstValueFrom(this.dialogRef.closed);
|
|
||||||
if (!closedWithButton) {
|
|
||||||
// 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.retrieveAuthRequestAndRespond(false);
|
|
||||||
}
|
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.notificationId != null) {
|
if (this.notificationId != null) {
|
||||||
|
try {
|
||||||
this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId);
|
this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId);
|
||||||
|
} catch (error) {
|
||||||
|
this.validationService.showError(error);
|
||||||
|
}
|
||||||
|
|
||||||
const publicKey = Utils.fromB64ToArray(this.authRequestResponse.publicKey);
|
const publicKey = Utils.fromB64ToArray(this.authRequestResponse.publicKey);
|
||||||
this.email = await await firstValueFrom(
|
this.email = await await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||||
@@ -96,6 +99,8 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
|
|||||||
}, RequestTimeUpdate);
|
}, RequestTimeUpdate);
|
||||||
|
|
||||||
this.loginApprovalComponentService.showLoginRequestedAlertIfWindowNotVisible(this.email);
|
this.loginApprovalComponentService.showLoginRequestedAlertIfWindowNotVisible(this.email);
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +136,8 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
this.showResultToast(loginResponse);
|
this.showResultToast(loginResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.dialogRef.close(approve);
|
||||||
}
|
}
|
||||||
|
|
||||||
showResultToast(loginResponse: AuthRequestResponse) {
|
showResultToast(loginResponse: AuthRequestResponse) {
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { DeviceType } from "../../../../enums";
|
import { DeviceType } from "../../../../enums";
|
||||||
import { BaseResponse } from "../../../../models/response/base.response";
|
import { BaseResponse } from "../../../../models/response/base.response";
|
||||||
|
|
||||||
|
export interface DevicePendingAuthRequest {
|
||||||
|
id: string;
|
||||||
|
creationDate: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class DeviceResponse extends BaseResponse {
|
export class DeviceResponse extends BaseResponse {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -10,7 +15,7 @@ export class DeviceResponse extends BaseResponse {
|
|||||||
creationDate: string;
|
creationDate: string;
|
||||||
revisionDate: string;
|
revisionDate: string;
|
||||||
isTrusted: boolean;
|
isTrusted: boolean;
|
||||||
devicePendingAuthRequest: { id: string; creationDate: string } | null;
|
devicePendingAuthRequest: DevicePendingAuthRequest | null;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
|
|||||||
Reference in New Issue
Block a user