From 1d04a0a3998b3a83258c2a2350ebdd33b1dbe424 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Thu, 19 Dec 2024 09:59:42 -0500 Subject: [PATCH] [PM-8214] New Device Verification Notice UI (#12360) * starting * setup first page for new device verification notice * update designs for first page. rename components and files * added second page for new device verification notice * update notice page one with bit radio buttons. routing logic. user email * updated routing for new device verification notice to show before vault based on flags, and can navigate back to vault after submission * fix translations. added remind me later link and nav to page 2 * sync the design for mobile and web * update routes in desktop * updated styles for desktop * moved new device verification notice guard * update types for new device notice page one * add null check to page one * types * types for page one, page two, service, and guard * types * update component and guard for null check * add navigation to two step login btn and account email btn * remove empty file * update fill of icons to support light & dark modes * add question mark to email access verification copy * remove unused map * use links for navigation elements - an empty href is needed so the links are keyboard accessible * remove clip path from exclamation svg - No noticeable difference in the end result * inline email message into markup --------- Co-authored-by: Nick Krantz --- apps/browser/src/_locales/en/messages.json | 36 +++++++ apps/browser/src/popup/app-routing.module.ts | 35 ++++++- apps/desktop/src/app/app-routing.module.ts | 35 ++++++- apps/desktop/src/locales/en/messages.json | 36 +++++++ apps/desktop/tailwind.config.js | 1 + apps/web/src/app/oss-routing.module.ts | 35 ++++++- apps/web/src/locales/en/messages.json | 36 +++++++ .../src/services/jslib-services.module.ts | 2 + libs/angular/src/vault/guards/index.ts | 1 + .../new-device-verification-notice.guard.ts | 51 ++++++++++ ...erification-notice-page-one.component.html | 30 ++++++ ...-verification-notice-page-one.component.ts | 82 ++++++++++++++++ ...erification-notice-page-two.component.html | 39 ++++++++ ...-verification-notice-page-two.component.ts | 95 +++++++++++++++++++ libs/vault/src/icons/exclamation-triangle.ts | 7 ++ libs/vault/src/icons/index.ts | 2 + libs/vault/src/icons/user-lock.ts | 17 ++++ libs/vault/src/index.ts | 2 + .../new-device-verification-notice.service.ts | 2 +- 19 files changed, 540 insertions(+), 4 deletions(-) create mode 100644 libs/angular/src/vault/guards/index.ts create mode 100644 libs/angular/src/vault/guards/new-device-verification-notice.guard.ts create mode 100644 libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.html create mode 100644 libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.ts create mode 100644 libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.html create mode 100644 libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.ts create mode 100644 libs/vault/src/icons/exclamation-triangle.ts create mode 100644 libs/vault/src/icons/user-lock.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index c2e9ef60d8c..de438a09467 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4910,6 +4910,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 3ec2667cd8c..ad839bbd7ce 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -19,6 +19,7 @@ import { import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { extensionRefreshRedirect } from "@bitwarden/angular/utils/extension-refresh-redirect"; import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap"; +import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, @@ -43,6 +44,11 @@ import { TwoFactorTimeoutIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { + NewDeviceVerificationNoticePageOneComponent, + NewDeviceVerificationNoticePageTwoComponent, + VaultIcons, +} from "@bitwarden/vault"; import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-factor-component-refactor-route-swap"; import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard"; @@ -715,6 +721,33 @@ const routes: Routes = [ canActivate: [authGuard], data: { elevation: 2 } satisfies RouteDataProperties, }, + { + path: "new-device-notice", + component: ExtensionAnonLayoutWrapperComponent, + canActivate: [], + children: [ + { + path: "", + component: NewDeviceVerificationNoticePageOneComponent, + data: { + pageIcon: VaultIcons.ExclamationTriangle, + pageTitle: { + key: "importantNotice", + }, + }, + }, + { + path: "setup", + component: NewDeviceVerificationNoticePageTwoComponent, + data: { + pageIcon: VaultIcons.UserLock, + pageTitle: { + key: "setupTwoStepLogin", + }, + }, + }, + ], + }, ...extensionRefreshSwap(TabsComponent, TabsV2Component, { path: "tabs", data: { elevation: 0 } satisfies RouteDataProperties, @@ -734,7 +767,7 @@ const routes: Routes = [ }, ...extensionRefreshSwap(VaultFilterComponent, VaultV2Component, { path: "vault", - canActivate: [authGuard], + canActivate: [authGuard, NewDeviceVerificationNoticeGuard], canDeactivate: [clearVaultStateGuard], data: { elevation: 0 } satisfies RouteDataProperties, }), diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 21dced5c2aa..c7642638dc3 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ import { } from "@bitwarden/angular/auth/guards"; import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { extensionRefreshRedirect } from "@bitwarden/angular/utils/extension-refresh-redirect"; +import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, @@ -40,6 +41,11 @@ import { TwoFactorTimeoutIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { + NewDeviceVerificationNoticePageOneComponent, + NewDeviceVerificationNoticePageTwoComponent, + VaultIcons, +} from "@bitwarden/vault"; import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-factor-component-refactor-route-swap"; import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.component"; @@ -116,10 +122,37 @@ const routes: Routes = [ } satisfies RouteDataProperties & AnonLayoutWrapperData, }, { path: "register", component: RegisterComponent }, + { + path: "new-device-notice", + component: AnonLayoutWrapperComponent, + canActivate: [], + children: [ + { + path: "", + component: NewDeviceVerificationNoticePageOneComponent, + data: { + pageIcon: VaultIcons.ExclamationTriangle, + pageTitle: { + key: "importantNotice", + }, + }, + }, + { + path: "setup", + component: NewDeviceVerificationNoticePageTwoComponent, + data: { + pageIcon: VaultIcons.UserLock, + pageTitle: { + key: "setupTwoStepLogin", + }, + }, + }, + ], + }, { path: "vault", component: VaultComponent, - canActivate: [authGuard], + canActivate: [authGuard, NewDeviceVerificationNoticeGuard], }, { path: "accessibility-cookie", component: AccessibilityCookieComponent }, { path: "set-password", component: SetPasswordComponent }, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index f8f81a5ac2c..323d0cd3f7b 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -3394,5 +3394,41 @@ }, "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." + }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" } } diff --git a/apps/desktop/tailwind.config.js b/apps/desktop/tailwind.config.js index db1dd55694e..a561b93b21a 100644 --- a/apps/desktop/tailwind.config.js +++ b/apps/desktop/tailwind.config.js @@ -6,6 +6,7 @@ config.content = [ "../../libs/components/src/**/*.{html,ts}", "../../libs/auth/src/**/*.{html,ts}", "../../libs/angular/src/**/*.{html,ts}", + "../../libs/vault/src/**/*.{html,ts,mdx}", ]; module.exports = config; diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 649f1aba534..9f2a86c1c06 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -13,6 +13,7 @@ import { import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { generatorSwap } from "@bitwarden/angular/tools/generator/generator-swap"; import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap"; +import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, @@ -40,6 +41,11 @@ import { LoginDecryptionOptionsComponent, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { + NewDeviceVerificationNoticePageOneComponent, + NewDeviceVerificationNoticePageTwoComponent, + VaultIcons, +} from "@bitwarden/vault"; import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-factor-component-refactor-route-swap"; import { flagEnabled, Flags } from "../utils/flags"; @@ -695,10 +701,37 @@ const routes: Routes = [ }, ], }, + { + path: "new-device-notice", + component: AnonLayoutWrapperComponent, + canActivate: [], + children: [ + { + path: "", + component: NewDeviceVerificationNoticePageOneComponent, + data: { + pageIcon: VaultIcons.ExclamationTriangle, + pageTitle: { + key: "importantNotice", + }, + }, + }, + { + path: "setup", + component: NewDeviceVerificationNoticePageTwoComponent, + data: { + pageIcon: VaultIcons.UserLock, + pageTitle: { + key: "setupTwoStepLogin", + }, + }, + }, + ], + }, { path: "", component: UserLayoutComponent, - canActivate: [deepLinkGuard(), authGuard], + canActivate: [deepLinkGuard(), authGuard, NewDeviceVerificationNoticeGuard], children: [ { path: "vault", diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index abd5779339f..acbb348048c 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -9888,6 +9888,42 @@ "descriptorCode": { "message": "Descriptor code" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "removeMembers": { "message": "Remove members" }, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 0e50cec1b64..0765fd8e4c6 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -298,6 +298,7 @@ import { IndividualVaultExportServiceAbstraction, } from "@bitwarden/vault-export-core"; +import { NewDeviceVerificationNoticeService } from "../../../vault/src/services/new-device-verification-notice.service"; import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service"; import { ViewCacheService } from "../platform/abstractions/view-cache.service"; import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service"; @@ -1401,6 +1402,7 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultLoginDecryptionOptionsService, deps: [MessagingServiceAbstraction], }), + safeProvider(NewDeviceVerificationNoticeService), safeProvider({ provide: UserAsymmetricKeysRegenerationApiService, useClass: DefaultUserAsymmetricKeysRegenerationApiService, diff --git a/libs/angular/src/vault/guards/index.ts b/libs/angular/src/vault/guards/index.ts new file mode 100644 index 00000000000..001a4832372 --- /dev/null +++ b/libs/angular/src/vault/guards/index.ts @@ -0,0 +1 @@ +export * from "./new-device-verification-notice.guard"; diff --git a/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts b/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts new file mode 100644 index 00000000000..a37097e3583 --- /dev/null +++ b/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts @@ -0,0 +1,51 @@ +import { inject } from "@angular/core"; +import { ActivatedRouteSnapshot, CanActivateFn, Router } from "@angular/router"; +import { Observable, firstValueFrom, map } from "rxjs"; + +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +import { NewDeviceVerificationNoticeService } from "../../../../vault/src/services/new-device-verification-notice.service"; + +export const NewDeviceVerificationNoticeGuard: CanActivateFn = async ( + route: ActivatedRouteSnapshot, +) => { + const router = inject(Router); + const configService = inject(ConfigService); + const newDeviceVerificationNoticeService = inject(NewDeviceVerificationNoticeService); + const accountService = inject(AccountService); + + if (route.queryParams["fromNewDeviceVerification"]) { + return true; + } + + const tempNoticeFlag = await configService.getFeatureFlag( + FeatureFlag.NewDeviceVerificationTemporaryDismiss, + ); + const permNoticeFlag = await configService.getFeatureFlag( + FeatureFlag.NewDeviceVerificationPermanentDismiss, + ); + + const currentAcct$: Observable = accountService.activeAccount$.pipe( + map((acct) => acct), + ); + const currentAcct = await firstValueFrom(currentAcct$); + + if (!currentAcct) { + return router.createUrlTree(["/login"]); + } + + const userItems$ = newDeviceVerificationNoticeService.noticeState$(currentAcct.id); + const userItems = await firstValueFrom(userItems$); + + if ( + userItems?.last_dismissal == null && + (userItems?.permanent_dismissal == null || !userItems?.permanent_dismissal) && + (tempNoticeFlag || permNoticeFlag) + ) { + return router.createUrlTree(["/new-device-notice"]); + } + + return true; +}; diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.html b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.html new file mode 100644 index 00000000000..316df3aed17 --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.html @@ -0,0 +1,30 @@ +
+

+ {{ "newDeviceVerificationNoticeContentPage1" | i18n }} +

+ + +

+ {{ "newDeviceVerificationNoticePageOneFormContent" | i18n: this.currentEmail }} +

+ + + + {{ "newDeviceVerificationNoticePageOneEmailAccessNo" | i18n }} + + + {{ "newDeviceVerificationNoticePageOneEmailAccessYes" | i18n }} + + +
+ + +
diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.ts b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.ts new file mode 100644 index 00000000000..62ae22f5b22 --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.ts @@ -0,0 +1,82 @@ +import { CommonModule } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, ReactiveFormsModule } from "@angular/forms"; +import { Router } from "@angular/router"; +import { firstValueFrom, Observable } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { ClientType } from "@bitwarden/common/enums"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { + AsyncActionsModule, + ButtonModule, + CardComponent, + FormFieldModule, + RadioButtonModule, + TypographyModule, +} from "@bitwarden/components"; + +import { NewDeviceVerificationNoticeService } from "./../../services/new-device-verification-notice.service"; + +@Component({ + standalone: true, + selector: "app-new-device-verification-notice-page-one", + templateUrl: "./new-device-verification-notice-page-one.component.html", + imports: [ + CardComponent, + CommonModule, + JslibModule, + TypographyModule, + ButtonModule, + RadioButtonModule, + FormFieldModule, + AsyncActionsModule, + ReactiveFormsModule, + ], +}) +export class NewDeviceVerificationNoticePageOneComponent implements OnInit { + protected formGroup = this.formBuilder.group({ + hasEmailAccess: new FormControl(0), + }); + protected isDesktop: boolean; + readonly currentAcct$: Observable = this.accountService.activeAccount$; + protected currentEmail: string = ""; + private currentUserId: UserId | null = null; + + constructor( + private formBuilder: FormBuilder, + private router: Router, + private accountService: AccountService, + private newDeviceVerificationNoticeService: NewDeviceVerificationNoticeService, + private platformUtilsService: PlatformUtilsService, + ) { + this.isDesktop = this.platformUtilsService.getClientType() === ClientType.Desktop; + } + + async ngOnInit() { + const currentAcct = await firstValueFrom(this.currentAcct$); + if (!currentAcct) { + return; + } + this.currentEmail = currentAcct.email; + this.currentUserId = currentAcct.id; + } + + submit = async () => { + if (this.formGroup.controls.hasEmailAccess.value === 0) { + await this.router.navigate(["new-device-notice/setup"]); + } else if (this.formGroup.controls.hasEmailAccess.value === 1) { + await this.newDeviceVerificationNoticeService.updateNewDeviceVerificationNoticeState( + this.currentUserId, + { + last_dismissal: new Date(), + permanent_dismissal: false, + }, + ); + + await this.router.navigate(["/vault"]); + } + }; +} diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.html b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.html new file mode 100644 index 00000000000..270b4126252 --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.html @@ -0,0 +1,39 @@ +

+ {{ "newDeviceVerificationNoticeContentPage2" | i18n }} +

+ + + {{ "turnOnTwoStepLogin" | i18n }} + + + + {{ "changeAcctEmail" | i18n }} + + + + diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.ts b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.ts new file mode 100644 index 00000000000..630a2fd516c --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.ts @@ -0,0 +1,95 @@ +import { CommonModule } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { Router } from "@angular/router"; +import { firstValueFrom, Observable } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { ClientType } from "@bitwarden/common/enums"; +import { + Environment, + EnvironmentService, +} from "@bitwarden/common/platform/abstractions/environment.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { ButtonModule, LinkModule, TypographyModule } from "@bitwarden/components"; + +import { NewDeviceVerificationNoticeService } from "../../services/new-device-verification-notice.service"; + +@Component({ + standalone: true, + selector: "app-new-device-verification-notice-page-two", + templateUrl: "./new-device-verification-notice-page-two.component.html", + imports: [CommonModule, JslibModule, TypographyModule, ButtonModule, LinkModule], +}) +export class NewDeviceVerificationNoticePageTwoComponent implements OnInit { + protected isWeb: boolean; + protected isDesktop: boolean; + readonly currentAcct$: Observable = this.accountService.activeAccount$; + private currentUserId: UserId | null = null; + private env$: Observable = this.environmentService.environment$; + + constructor( + private newDeviceVerificationNoticeService: NewDeviceVerificationNoticeService, + private router: Router, + private accountService: AccountService, + private platformUtilsService: PlatformUtilsService, + private environmentService: EnvironmentService, + ) { + this.isWeb = this.platformUtilsService.getClientType() === ClientType.Web; + this.isDesktop = this.platformUtilsService.getClientType() === ClientType.Desktop; + } + + async ngOnInit() { + const currentAcct = await firstValueFrom(this.currentAcct$); + if (!currentAcct) { + return; + } + this.currentUserId = currentAcct.id; + } + + async navigateToTwoStepLogin(event: Event) { + event.preventDefault(); + + const env = await firstValueFrom(this.env$); + const url = env.getWebVaultUrl(); + + if (this.isWeb) { + await this.router.navigate(["/settings/security/two-factor"], { + queryParams: { fromNewDeviceVerification: true }, + }); + } else { + this.platformUtilsService.launchUri( + url + "/#/settings/security/two-factor/?fromNewDeviceVerification=true", + ); + } + } + + async navigateToChangeAcctEmail(event: Event) { + event.preventDefault(); + + const env = await firstValueFrom(this.env$); + const url = env.getWebVaultUrl(); + if (this.isWeb) { + await this.router.navigate(["/settings/account"], { + queryParams: { fromNewDeviceVerification: true }, + }); + } else { + this.platformUtilsService.launchUri( + url + "/#/settings/account/?fromNewDeviceVerification=true", + ); + } + } + + async remindMeLaterSelect() { + await this.newDeviceVerificationNoticeService.updateNewDeviceVerificationNoticeState( + this.currentUserId, + { + last_dismissal: new Date(), + permanent_dismissal: false, + }, + ); + + await this.router.navigate(["/vault"]); + } +} diff --git a/libs/vault/src/icons/exclamation-triangle.ts b/libs/vault/src/icons/exclamation-triangle.ts new file mode 100644 index 00000000000..6340546d1e1 --- /dev/null +++ b/libs/vault/src/icons/exclamation-triangle.ts @@ -0,0 +1,7 @@ +import { svgIcon } from "@bitwarden/components"; + +export const ExclamationTriangle = svgIcon` + + + +`; diff --git a/libs/vault/src/icons/index.ts b/libs/vault/src/icons/index.ts index c1b69a31ef5..2e106782f53 100644 --- a/libs/vault/src/icons/index.ts +++ b/libs/vault/src/icons/index.ts @@ -2,3 +2,5 @@ export * from "./deactivated-org"; export * from "./no-folders"; export * from "./vault"; export * from "./empty-trash"; +export * from "./exclamation-triangle"; +export * from "./user-lock"; diff --git a/libs/vault/src/icons/user-lock.ts b/libs/vault/src/icons/user-lock.ts new file mode 100644 index 00000000000..c1dc3efde39 --- /dev/null +++ b/libs/vault/src/icons/user-lock.ts @@ -0,0 +1,17 @@ +import { svgIcon } from "@bitwarden/components"; + +export const UserLock = svgIcon` + + + + + + + + + + + + + +`; diff --git a/libs/vault/src/index.ts b/libs/vault/src/index.ts index dca9b2dee79..0112de44241 100644 --- a/libs/vault/src/index.ts +++ b/libs/vault/src/index.ts @@ -14,5 +14,7 @@ export { export { DownloadAttachmentComponent } from "./components/download-attachment/download-attachment.component"; export { PasswordHistoryViewComponent } from "./components/password-history-view/password-history-view.component"; +export { NewDeviceVerificationNoticePageOneComponent } from "./components/new-device-verification-notice/new-device-verification-notice-page-one.component"; +export { NewDeviceVerificationNoticePageTwoComponent } from "./components/new-device-verification-notice/new-device-verification-notice-page-two.component"; export * as VaultIcons from "./icons"; diff --git a/libs/vault/src/services/new-device-verification-notice.service.ts b/libs/vault/src/services/new-device-verification-notice.service.ts index 6c7df590b50..bb096ff0c2c 100644 --- a/libs/vault/src/services/new-device-verification-notice.service.ts +++ b/libs/vault/src/services/new-device-verification-notice.service.ts @@ -57,7 +57,7 @@ export class NewDeviceVerificationNoticeService { } async updateNewDeviceVerificationNoticeState( - userId: UserId, + userId: UserId | null, newState: NewDeviceVerificationNotice, ): Promise { await this.noticeState(userId).update(() => {