Skip to content

Commit

Permalink
[PM-8214] New Device Verification Notice UI (#12360)
Browse files Browse the repository at this point in the history
* 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 <nick@livefront.com>
  • Loading branch information
Jingo88 and nick-livefront authored Dec 19, 2024
1 parent 23212fb commit 1d04a0a
Show file tree
Hide file tree
Showing 19 changed files with 540 additions and 4 deletions.
36 changes: 36 additions & 0 deletions apps/browser/src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
35 changes: 34 additions & 1 deletion apps/browser/src/popup/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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";
Expand Down Expand Up @@ -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,
Expand All @@ -734,7 +767,7 @@ const routes: Routes = [
},
...extensionRefreshSwap(VaultFilterComponent, VaultV2Component, {
path: "vault",
canActivate: [authGuard],
canActivate: [authGuard, NewDeviceVerificationNoticeGuard],
canDeactivate: [clearVaultStateGuard],
data: { elevation: 0 } satisfies RouteDataProperties,
}),
Expand Down
35 changes: 34 additions & 1 deletion apps/desktop/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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";
Expand Down Expand Up @@ -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 },
Expand Down
36 changes: 36 additions & 0 deletions apps/desktop/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
1 change: 1 addition & 0 deletions apps/desktop/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
35 changes: 34 additions & 1 deletion apps/web/src/app/oss-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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",
Expand Down
36 changes: 36 additions & 0 deletions apps/web/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
2 changes: 2 additions & 0 deletions libs/angular/src/services/jslib-services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -1401,6 +1402,7 @@ const safeProviders: SafeProvider[] = [
useClass: DefaultLoginDecryptionOptionsService,
deps: [MessagingServiceAbstraction],
}),
safeProvider(NewDeviceVerificationNoticeService),
safeProvider({
provide: UserAsymmetricKeysRegenerationApiService,
useClass: DefaultUserAsymmetricKeysRegenerationApiService,
Expand Down
1 change: 1 addition & 0 deletions libs/angular/src/vault/guards/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./new-device-verification-notice.guard";
Original file line number Diff line number Diff line change
@@ -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<Account | null> = 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;
};
Loading

0 comments on commit 1d04a0a

Please sign in to comment.