mirror of
https://github.com/bitwarden/browser
synced 2026-02-09 13:10:17 +00:00
Move email sent information to its own cache service
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { inject, Injectable, WritableSignal } from "@angular/core";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
||||
const TWO_FACTOR_AUTH_EMAIL_CACHE_KEY = "two-factor-auth-email-cache";
|
||||
|
||||
/**
|
||||
* Cache model for the email two factor
|
||||
*/
|
||||
export class TwoFactorAuthEmailCache {
|
||||
emailSent: boolean = false;
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<TwoFactorAuthEmailCache>>): TwoFactorAuthEmailCache {
|
||||
return Object.assign(new TwoFactorAuthEmailCache(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache service for the two factor auth email component.
|
||||
*/
|
||||
@Injectable()
|
||||
export class TwoFactorAuthEmailComponentCacheService {
|
||||
private viewCacheService: ViewCacheService = inject(ViewCacheService);
|
||||
private configService: ConfigService = inject(ConfigService);
|
||||
|
||||
/** True when the feature flag is enabled */
|
||||
private featureEnabled: boolean = false;
|
||||
|
||||
/**
|
||||
* Signal for the cached email state.
|
||||
*/
|
||||
private emailCache: WritableSignal<TwoFactorAuthEmailCache | null> =
|
||||
this.viewCacheService.signal<TwoFactorAuthEmailCache | null>({
|
||||
key: TWO_FACTOR_AUTH_EMAIL_CACHE_KEY,
|
||||
initialValue: null,
|
||||
deserializer: TwoFactorAuthEmailCache.fromJSON,
|
||||
});
|
||||
|
||||
/**
|
||||
* Must be called once before interacting with the cached data.
|
||||
*/
|
||||
async init() {
|
||||
this.featureEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.PM9115_TwoFactorExtensionDataPersistence,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache the email sent state.
|
||||
*/
|
||||
cacheData(data: { emailSent: boolean }): void {
|
||||
if (!this.featureEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emailCache.set({
|
||||
emailSent: data.emailSent,
|
||||
} as TwoFactorAuthEmailCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cached email data.
|
||||
*/
|
||||
clearCachedData(): void {
|
||||
if (!this.featureEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emailCache.set(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the email has been sent.
|
||||
*/
|
||||
getCachedData(): TwoFactorAuthEmailCache | null {
|
||||
if (!this.featureEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.emailCache();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ToastService,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { TwoFactorAuthEmailComponentCacheService } from "./two-factor-auth-email-cache.service";
|
||||
import { TwoFactorAuthEmailComponentService } from "./two-factor-auth-email-component.service";
|
||||
|
||||
@Component({
|
||||
@@ -40,15 +41,19 @@ import { TwoFactorAuthEmailComponentService } from "./two-factor-auth-email-comp
|
||||
AsyncActionsModule,
|
||||
FormsModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: TwoFactorAuthEmailComponentCacheService,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class TwoFactorAuthEmailComponent implements OnInit {
|
||||
@Input({ required: true }) tokenFormControl: FormControl | undefined = undefined;
|
||||
@Input({ required: true }) emailSent: boolean = false;
|
||||
@Output() tokenChange = new EventEmitter<{ token: string }>();
|
||||
@Output() emailSendEvent = new EventEmitter<void>();
|
||||
|
||||
twoFactorEmail: string | undefined = undefined;
|
||||
emailPromise: Promise<any> | undefined;
|
||||
emailSent = false;
|
||||
|
||||
constructor(
|
||||
protected i18nService: I18nService,
|
||||
@@ -60,10 +65,18 @@ export class TwoFactorAuthEmailComponent implements OnInit {
|
||||
protected appIdService: AppIdService,
|
||||
private toastService: ToastService,
|
||||
private twoFactorAuthEmailComponentService: TwoFactorAuthEmailComponentService,
|
||||
private cacheService: TwoFactorAuthEmailComponentCacheService,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this.twoFactorAuthEmailComponentService.openPopoutIfApprovedForEmail2fa?.();
|
||||
await this.cacheService.init();
|
||||
|
||||
// Check if email was already sent
|
||||
const cachedData = this.cacheService.getCachedData();
|
||||
if (cachedData?.emailSent) {
|
||||
this.emailSent = true;
|
||||
}
|
||||
|
||||
const providers = await this.twoFactorService.getProviders();
|
||||
|
||||
@@ -124,7 +137,8 @@ export class TwoFactorAuthEmailComponent implements OnInit {
|
||||
this.emailPromise = this.apiService.postTwoFactorEmail(request);
|
||||
await this.emailPromise;
|
||||
|
||||
this.emailSendEvent.emit();
|
||||
this.emailSent = true;
|
||||
this.cacheService.cacheData({ emailSent: this.emailSent });
|
||||
|
||||
if (doToast) {
|
||||
this.toastService.showToast({
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
<app-two-factor-auth-email
|
||||
[tokenFormControl]="tokenFormControl"
|
||||
(tokenChange)="saveFormDataWithPartialData($event)"
|
||||
[emailSent]="emailSent"
|
||||
(emailSendEvent)="saveFormDataWithPartialData({ emailSent: true })"
|
||||
*ngIf="selectedProviderType === providerType.Email"
|
||||
/>
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ interface TwoFactorCacheData {
|
||||
token?: string;
|
||||
remember?: boolean;
|
||||
selectedProviderType?: TwoFactorProviderType;
|
||||
emailSent?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -114,11 +113,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
|
||||
loading = true;
|
||||
|
||||
/**
|
||||
* Whether the email has been sent according to the cache
|
||||
*/
|
||||
emailSent = false;
|
||||
|
||||
orgSsoIdentifier: string | undefined = undefined;
|
||||
|
||||
providerType = TwoFactorProviderType;
|
||||
@@ -206,9 +200,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
this.selectedProviderType = persistedData.selectedProviderType;
|
||||
loadedCachedProviderType = true;
|
||||
}
|
||||
if (persistedData.emailSent !== undefined) {
|
||||
this.emailSent = persistedData.emailSent;
|
||||
}
|
||||
}
|
||||
|
||||
// Only set default 2FA provider type if we don't have one from cache
|
||||
@@ -242,7 +233,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
data?.selectedProviderType ??
|
||||
currentData?.selectedProviderType ??
|
||||
TwoFactorProviderType.Authenticator,
|
||||
emailSent: data?.emailSent ?? currentData?.emailSent ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -254,7 +244,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
token: this.tokenFormControl.value || undefined,
|
||||
remember: this.rememberFormControl.value ?? undefined,
|
||||
selectedProviderType: this.selectedProviderType,
|
||||
emailSent: this.selectedProviderType === TwoFactorProviderType.Email,
|
||||
};
|
||||
|
||||
await this.saveFormDataWithPartialData(formData);
|
||||
@@ -351,7 +340,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
token: tokenValue,
|
||||
remember: rememberValue,
|
||||
selectedProviderType: this.selectedProviderType,
|
||||
emailSent: this.selectedProviderType === TwoFactorProviderType.Email,
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -379,7 +367,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
token: "",
|
||||
remember: false,
|
||||
selectedProviderType: this.selectedProviderType,
|
||||
emailSent: false,
|
||||
});
|
||||
|
||||
const dialogRef = TwoFactorOptionsComponent.open(this.dialogService);
|
||||
@@ -400,7 +387,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
token: "",
|
||||
remember: false,
|
||||
selectedProviderType: response.type,
|
||||
emailSent: false,
|
||||
});
|
||||
|
||||
this.form.reset();
|
||||
|
||||
@@ -15,7 +15,6 @@ export class TwoFactorAuthCache {
|
||||
token: string | undefined = undefined;
|
||||
remember: boolean | undefined = undefined;
|
||||
selectedProviderType: TwoFactorProviderType | undefined = undefined;
|
||||
emailSent: boolean | undefined = undefined;
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<TwoFactorAuthCache>>): TwoFactorAuthCache {
|
||||
return Object.assign(new TwoFactorAuthCache(), obj);
|
||||
@@ -26,7 +25,6 @@ export interface TwoFactorAuthData {
|
||||
token?: string;
|
||||
remember?: boolean;
|
||||
selectedProviderType?: TwoFactorProviderType;
|
||||
emailSent?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +74,6 @@ export class TwoFactorAuthComponentCacheService {
|
||||
token: data.token,
|
||||
remember: data.remember,
|
||||
selectedProviderType: data.selectedProviderType,
|
||||
emailSent: data.emailSent,
|
||||
} as TwoFactorAuthCache);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user