From d0bc2b972d2f5138d92193ec64c1c409153da25c Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:46:02 -0700 Subject: [PATCH 01/28] rename legacy components to v1 --- ...nent.html => login-decryption-options-v1.component.html} | 0 ...omponent.ts => login-decryption-options-v1.component.ts} | 6 +++--- apps/browser/src/popup/app-routing.module.ts | 4 ++-- apps/browser/src/popup/app.module.ts | 4 ++-- apps/desktop/src/app/app-routing.module.ts | 4 ++-- ...nent.html => login-decryption-options-v1.component.html} | 0 ...omponent.ts => login-decryption-options-v1.component.ts} | 6 +++--- apps/desktop/src/auth/login/login.module.ts | 4 ++-- ...nent.html => login-decryption-options-v1.component.html} | 0 ...omponent.ts => login-decryption-options-v1.component.ts} | 6 +++--- apps/web/src/app/auth/login/login.module.ts | 6 +++--- apps/web/src/app/oss-routing.module.ts | 4 ++-- ...ent.ts => base-login-decryption-options-v1.component.ts} | 2 +- 13 files changed, 23 insertions(+), 23 deletions(-) rename apps/browser/src/auth/popup/login-decryption-options/{login-decryption-options.component.html => login-decryption-options-v1.component.html} (100%) rename apps/browser/src/auth/popup/login-decryption-options/{login-decryption-options.component.ts => login-decryption-options-v1.component.ts} (80%) rename apps/desktop/src/auth/login/login-decryption-options/{login-decryption-options.component.html => login-decryption-options-v1.component.html} (100%) rename apps/desktop/src/auth/login/login-decryption-options/{login-decryption-options.component.ts => login-decryption-options-v1.component.ts} (56%) rename apps/web/src/app/auth/login/login-decryption-options/{login-decryption-options.component.html => login-decryption-options-v1.component.html} (100%) rename apps/web/src/app/auth/login/login-decryption-options/{login-decryption-options.component.ts => login-decryption-options-v1.component.ts} (78%) rename libs/angular/src/auth/components/{base-login-decryption-options.component.ts => base-login-decryption-options-v1.component.ts} (99%) diff --git a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options-v1.component.html similarity index 100% rename from apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html rename to apps/browser/src/auth/popup/login-decryption-options/login-decryption-options-v1.component.html diff --git a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options-v1.component.ts similarity index 80% rename from apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts rename to apps/browser/src/auth/popup/login-decryption-options/login-decryption-options-v1.component.ts index 6231b027749..bd8f808c910 100644 --- a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts +++ b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options-v1.component.ts @@ -1,15 +1,15 @@ import { Component } from "@angular/core"; import { firstValueFrom } from "rxjs"; -import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; +import { BaseLoginDecryptionOptionsComponentV1 } from "@bitwarden/angular/auth/components/base-login-decryption-options-v1.component"; import { postLogoutMessageListener$ } from "../utils/post-logout-message-listener"; @Component({ selector: "browser-login-decryption-options", - templateUrl: "login-decryption-options.component.html", + templateUrl: "login-decryption-options-v1.component.html", }) -export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { +export class LoginDecryptionOptionsComponentV1 extends BaseLoginDecryptionOptionsComponentV1 { override async createUser(): Promise { try { await super.createUser(); diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index d53e51e9df2..504abdf8f55 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -49,7 +49,7 @@ import { import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; import { LockComponent } from "../auth/popup/lock.component"; -import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component"; +import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/popup/login-v1.component"; import { LoginViaAuthRequestComponent } from "../auth/popup/login-via-auth-request.component"; import { RegisterComponent } from "../auth/popup/register.component"; @@ -218,7 +218,7 @@ const routes: Routes = [ }, { path: "login-initiated", - component: LoginDecryptionOptionsComponent, + component: LoginDecryptionOptionsComponentV1, canActivate: [tdeDecryptionRequiredGuard()], data: { state: "login-initiated" } satisfies RouteDataProperties, }, diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 7b2d2ba86b8..e4f9a985f95 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -24,7 +24,7 @@ import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-ano import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; import { LockComponent } from "../auth/popup/lock.component"; -import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component"; +import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/popup/login-v1.component"; import { LoginViaAuthRequestComponent } from "../auth/popup/login-via-auth-request.component"; import { RegisterComponent } from "../auth/popup/register.component"; @@ -161,7 +161,7 @@ import "../platform/popup/locales"; LockComponent, LoginComponentV1, LoginViaAuthRequestComponent, - LoginDecryptionOptionsComponent, + LoginDecryptionOptionsComponentV1, NotificationsSettingsV1Component, AppearanceComponent, GeneratorComponent, diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index f5023cb4249..83c2628f281 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -40,7 +40,7 @@ import { AccessibilityCookieComponent } from "../auth/accessibility-cookie.compo import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; import { HintComponent } from "../auth/hint.component"; import { LockComponent } from "../auth/lock.component"; -import { LoginDecryptionOptionsComponent } from "../auth/login/login-decryption-options/login-decryption-options.component"; +import { LoginDecryptionOptionsComponentV1 } from "../auth/login/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/login/login-v1.component"; import { LoginViaAuthRequestComponent } from "../auth/login/login-via-auth-request.component"; import { RegisterComponent } from "../auth/register.component"; @@ -103,7 +103,7 @@ const routes: Routes = [ ), { path: "login-initiated", - component: LoginDecryptionOptionsComponent, + component: LoginDecryptionOptionsComponentV1, canActivate: [tdeDecryptionRequiredGuard()], }, { path: "register", component: RegisterComponent }, diff --git a/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.html b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options-v1.component.html similarity index 100% rename from apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.html rename to apps/desktop/src/auth/login/login-decryption-options/login-decryption-options-v1.component.html diff --git a/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options-v1.component.ts similarity index 56% rename from apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts rename to apps/desktop/src/auth/login/login-decryption-options/login-decryption-options-v1.component.ts index f64ec977ce7..d9cc07adb7e 100644 --- a/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts +++ b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options-v1.component.ts @@ -1,12 +1,12 @@ import { Component } from "@angular/core"; -import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; +import { BaseLoginDecryptionOptionsComponentV1 } from "@bitwarden/angular/auth/components/base-login-decryption-options-v1.component"; @Component({ selector: "desktop-login-decryption-options", - templateUrl: "login-decryption-options.component.html", + templateUrl: "login-decryption-options-v1.component.html", }) -export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { +export class LoginDecryptionOptionsComponentV1 extends BaseLoginDecryptionOptionsComponentV1 { override async createUser(): Promise { try { await super.createUser(); diff --git a/apps/desktop/src/auth/login/login.module.ts b/apps/desktop/src/auth/login/login.module.ts index c0b330bf2dd..a1c58ec5622 100644 --- a/apps/desktop/src/auth/login/login.module.ts +++ b/apps/desktop/src/auth/login/login.module.ts @@ -5,7 +5,7 @@ import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components import { SharedModule } from "../../app/shared/shared.module"; -import { LoginDecryptionOptionsComponent } from "./login-decryption-options/login-decryption-options.component"; +import { LoginDecryptionOptionsComponentV1 } from "./login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "./login-v1.component"; import { LoginViaAuthRequestComponent } from "./login-via-auth-request.component"; @@ -15,7 +15,7 @@ import { LoginViaAuthRequestComponent } from "./login-via-auth-request.component LoginComponentV1, LoginViaAuthRequestComponent, EnvironmentSelectorComponent, - LoginDecryptionOptionsComponent, + LoginDecryptionOptionsComponentV1, ], exports: [LoginComponentV1, LoginViaAuthRequestComponent], }) diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options-v1.component.html similarity index 100% rename from apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html rename to apps/web/src/app/auth/login/login-decryption-options/login-decryption-options-v1.component.html diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options-v1.component.ts similarity index 78% rename from apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts rename to apps/web/src/app/auth/login/login-decryption-options/login-decryption-options-v1.component.ts index 991fe8b5971..5eb72503b90 100644 --- a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts +++ b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options-v1.component.ts @@ -1,14 +1,14 @@ import { Component, inject } from "@angular/core"; -import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; +import { BaseLoginDecryptionOptionsComponentV1 } from "@bitwarden/angular/auth/components/base-login-decryption-options-v1.component"; import { RouterService } from "../../../core"; import { AcceptOrganizationInviteService } from "../../organization-invite/accept-organization.service"; @Component({ selector: "web-login-decryption-options", - templateUrl: "login-decryption-options.component.html", + templateUrl: "login-decryption-options-v1.component.html", }) -export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { +export class LoginDecryptionOptionsComponentV1 extends BaseLoginDecryptionOptionsComponentV1 { protected routerService = inject(RouterService); protected acceptOrganizationInviteService = inject(AcceptOrganizationInviteService); diff --git a/apps/web/src/app/auth/login/login.module.ts b/apps/web/src/app/auth/login/login.module.ts index 53e921d7d2b..6dbfaa9f370 100644 --- a/apps/web/src/app/auth/login/login.module.ts +++ b/apps/web/src/app/auth/login/login.module.ts @@ -4,7 +4,7 @@ import { CheckboxModule } from "@bitwarden/components"; import { SharedModule } from "../../../app/shared"; -import { LoginDecryptionOptionsComponent } from "./login-decryption-options/login-decryption-options.component"; +import { LoginDecryptionOptionsComponentV1 } from "./login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "./login-v1.component"; import { LoginViaAuthRequestComponent } from "./login-via-auth-request.component"; import { LoginViaWebAuthnComponent } from "./login-via-webauthn/login-via-webauthn.component"; @@ -14,13 +14,13 @@ import { LoginViaWebAuthnComponent } from "./login-via-webauthn/login-via-webaut declarations: [ LoginComponentV1, LoginViaAuthRequestComponent, - LoginDecryptionOptionsComponent, + LoginDecryptionOptionsComponentV1, LoginViaWebAuthnComponent, ], exports: [ LoginComponentV1, LoginViaAuthRequestComponent, - LoginDecryptionOptionsComponent, + LoginDecryptionOptionsComponentV1, LoginViaWebAuthnComponent, ], }) diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 71d26030b03..e1b28404517 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -44,7 +44,7 @@ import { CreateOrganizationComponent } from "./admin-console/settings/create-org import { deepLinkGuard } from "./auth/guards/deep-link.guard"; import { HintComponent } from "./auth/hint.component"; import { LockComponent } from "./auth/lock.component"; -import { LoginDecryptionOptionsComponent } from "./auth/login/login-decryption-options/login-decryption-options.component"; +import { LoginDecryptionOptionsComponentV1 } from "./auth/login/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "./auth/login/login-v1.component"; import { LoginViaAuthRequestComponent } from "./auth/login/login-via-auth-request.component"; import { LoginViaWebAuthnComponent } from "./auth/login/login-via-webauthn/login-via-webauthn.component"; @@ -113,7 +113,7 @@ const routes: Routes = [ }, { path: "login-initiated", - component: LoginDecryptionOptionsComponent, + component: LoginDecryptionOptionsComponentV1, canActivate: [tdeDecryptionRequiredGuard()], }, { diff --git a/libs/angular/src/auth/components/base-login-decryption-options.component.ts b/libs/angular/src/auth/components/base-login-decryption-options-v1.component.ts similarity index 99% rename from libs/angular/src/auth/components/base-login-decryption-options.component.ts rename to libs/angular/src/auth/components/base-login-decryption-options-v1.component.ts index f674a32af8b..df99503b6d7 100644 --- a/libs/angular/src/auth/components/base-login-decryption-options.component.ts +++ b/libs/angular/src/auth/components/base-login-decryption-options-v1.component.ts @@ -63,7 +63,7 @@ type ExistingUserUntrustedDeviceData = { type Data = NewUserData | ExistingUserUntrustedDeviceData; @Directive() -export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { +export class BaseLoginDecryptionOptionsComponentV1 implements OnInit, OnDestroy { private destroy$ = new Subject(); protected State = State; From f453cc8b6748d62148ca10a413db35f97db3d52e Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:21:37 -0700 Subject: [PATCH 02/28] setup new component and routes --- apps/browser/src/_locales/en/messages.json | 6 ++++ apps/browser/src/popup/app-routing.module.ts | 30 +++++++++++++++---- apps/desktop/src/app/app-routing.module.ts | 28 +++++++++++++---- apps/desktop/src/locales/en/messages.json | 6 ++++ apps/web/src/app/oss-routing.module.ts | 28 +++++++++++++---- apps/web/src/locales/en/messages.json | 6 ++++ libs/auth/src/angular/index.ts | 3 ++ .../login-decryption-options.component.html | 1 + .../login-decryption-options.component.ts | 8 +++++ libs/common/src/enums/feature-flag.enum.ts | 2 +- 10 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html create mode 100644 libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 92fabdae3ee..330fa66af05 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3240,6 +3240,12 @@ "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 504abdf8f55..a73dac00e9e 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -35,6 +35,7 @@ import { SetPasswordJitComponent, UserLockIcon, VaultIcon, + LoginDecryptionOptionsComponent, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -216,12 +217,6 @@ const routes: Routes = [ canActivate: [unauthGuardFn(unauthRouteOverrides)], data: { state: "2fa-options" } satisfies RouteDataProperties, }, - { - path: "login-initiated", - component: LoginDecryptionOptionsComponentV1, - canActivate: [tdeDecryptionRequiredGuard()], - data: { state: "login-initiated" } satisfies RouteDataProperties, - }, { path: "sso", component: SsoComponent, @@ -489,6 +484,29 @@ const routes: Routes = [ ], }, ), + ...unauthUiRefreshSwap( + LoginDecryptionOptionsComponentV1, + ExtensionAnonLayoutWrapperComponent, + { + path: "login-initiated", + canActivate: [tdeDecryptionRequiredGuard()], + data: { state: "login-initiated" } satisfies RouteDataProperties, + }, + { + path: "login-initiated", + canActivate: [tdeDecryptionRequiredGuard()], + data: { + pageTitle: { + key: "deviceApprovalRequiredV2", + }, + pageSubtitle: { + key: "selectAnApprovalOptionBelow", + }, + titleId: "deviceApprovalRequiredV2", + }, + children: [{ path: "", component: LoginDecryptionOptionsComponent }], + }, + ), { path: "", component: ExtensionAnonLayoutWrapperComponent, diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 83c2628f281..4d9ee1d69dc 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -32,6 +32,7 @@ import { SetPasswordJitComponent, UserLockIcon, VaultIcon, + LoginDecryptionOptionsComponent, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -101,11 +102,6 @@ const routes: Routes = [ ], }, ), - { - path: "login-initiated", - component: LoginDecryptionOptionsComponentV1, - canActivate: [tdeDecryptionRequiredGuard()], - }, { path: "register", component: RegisterComponent }, { path: "vault", @@ -200,6 +196,28 @@ const routes: Routes = [ ], }, ), + ...unauthUiRefreshSwap( + LoginDecryptionOptionsComponentV1, + AnonLayoutWrapperComponent, + { + path: "login-initiated", + canActivate: [tdeDecryptionRequiredGuard()], + }, + { + path: "login-initiated", + canActivate: [tdeDecryptionRequiredGuard()], + data: { + pageTitle: { + key: "deviceApprovalRequiredV2", + }, + pageSubtitle: { + key: "selectAnApprovalOptionBelow", + }, + titleId: "deviceApprovalRequiredV2", + }, + children: [{ path: "", component: LoginDecryptionOptionsComponent }], + }, + ), { path: "", component: AnonLayoutWrapperComponent, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 83aaf2157d7..f081632a6f7 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -2779,6 +2779,12 @@ "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index e1b28404517..d6c119d45e9 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -31,6 +31,7 @@ import { RegistrationLockAltIcon, RegistrationExpiredLinkIcon, VaultIcon, + LoginDecryptionOptionsComponent, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -111,11 +112,6 @@ const routes: Routes = [ component: LoginViaAuthRequestComponent, data: { titleId: "adminApprovalRequested" } satisfies RouteDataProperties, }, - { - path: "login-initiated", - component: LoginDecryptionOptionsComponentV1, - canActivate: [tdeDecryptionRequiredGuard()], - }, { path: "register", component: TrialInitiationComponent, @@ -226,6 +222,28 @@ const routes: Routes = [ ], }, ), + ...unauthUiRefreshSwap( + LoginDecryptionOptionsComponentV1, + AnonLayoutWrapperComponent, + { + path: "login-initiated", + canActivate: [tdeDecryptionRequiredGuard()], + }, + { + path: "login-initiated", + canActivate: [tdeDecryptionRequiredGuard()], + data: { + pageTitle: { + key: "deviceApprovalRequiredV2", + }, + pageSubtitle: { + key: "selectAnApprovalOptionBelow", + }, + titleId: "deviceApprovalRequiredV2", + }, + children: [{ path: "", component: LoginDecryptionOptionsComponent }], + }, + ), ...unauthUiRefreshSwap( AnonLayoutWrapperComponent, AnonLayoutWrapperComponent, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index aa7bce04312..bf09fb37ae7 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -7928,6 +7928,12 @@ "deviceApprovalRequired": { "message": "Device approval required. Select an approval option below:" }, + "deviceApprovalRequiredV2": { + "message": "Device approval required" + }, + "selectAnApprovalOptionBelow": { + "message": "Select an approval option below" + }, "rememberThisDevice": { "message": "Remember this device" }, diff --git a/libs/auth/src/angular/index.ts b/libs/auth/src/angular/index.ts index d3d9e600918..0deb9327869 100644 --- a/libs/auth/src/angular/index.ts +++ b/libs/auth/src/angular/index.ts @@ -24,6 +24,9 @@ export * from "./login/login-secondary-content.component"; export * from "./login/login-component.service"; export * from "./login/default-login-component.service"; +// login-decryption-options +export * from "./login-decryption-options/login-decryption-options.component"; + // password callout export * from "./password-callout/password-callout.component"; diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html new file mode 100644 index 00000000000..06e454b7c21 --- /dev/null +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -0,0 +1 @@ +
login-decryption-options works!
diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts new file mode 100644 index 00000000000..2304b6b9313 --- /dev/null +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -0,0 +1,8 @@ +import { Component } from "@angular/core"; + +@Component({ + standalone: true, + templateUrl: "./login-decryption-options.component.html", + imports: [], +}) +export class LoginDecryptionOptionsComponent {} diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 84cf5ed521e..7f3cb1893b5 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -68,7 +68,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.ProviderClientVaultPrivacyBanner]: FALSE, [FeatureFlag.VaultBulkManagementAction]: FALSE, [FeatureFlag.IdpAutoSubmitLogin]: FALSE, - [FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE, + [FeatureFlag.UnauthenticatedExtensionUIRefresh]: true, [FeatureFlag.EnableUpgradePasswordManagerSub]: FALSE, [FeatureFlag.GenerateIdentityFillScriptRefactor]: FALSE, [FeatureFlag.EnableNewCardCombinedExpiryAutofill]: FALSE, From d8927de4c9543134958144c2f528c883dd60fa5a Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:25:28 -0700 Subject: [PATCH 03/28] add template --- .../login-decryption-options.component.html | 35 +++++++++++++++++- .../login-decryption-options.component.ts | 36 +++++++++++++++++-- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index 06e454b7c21..d68c04609fe 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -1 +1,34 @@ -
login-decryption-options works!
+ +
+ + {{ "loading" | i18n }} +
+
+ +
+ + + {{ "rememberThisDevice" | i18n }} + {{ "uncheckIfPublicDevice" | i18n }} + + +
+ + +
{{ "or" | i18n }}
+ + + + +
+
diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 2304b6b9313..2445c06fe3b 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -1,8 +1,38 @@ -import { Component } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { + AsyncActionsModule, + ButtonModule, + CheckboxModule, + FormFieldModule, +} from "@bitwarden/components"; @Component({ standalone: true, templateUrl: "./login-decryption-options.component.html", - imports: [], + imports: [ + AsyncActionsModule, + ButtonModule, + CheckboxModule, + CommonModule, + FormFieldModule, + JslibModule, + ReactiveFormsModule, + ], }) -export class LoginDecryptionOptionsComponent {} +export class LoginDecryptionOptionsComponent implements OnInit { + loading = false; + + formGroup = this.formBuilder.group({ + rememberDevice: [true], + }); + + constructor(private formBuilder: FormBuilder) {} + + ngOnInit(): void {} + + submit = () => {}; +} From 32148cf34edb5a201b14c693e3f5c6d668981d14 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:40:58 -0700 Subject: [PATCH 04/28] setup rememberDevice control methods --- .../login-decryption-options.component.ts | 76 ++++++++++++++++++- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 2445c06fe3b..e913076bd11 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -1,13 +1,21 @@ import { CommonModule } from "@angular/common"; import { Component, OnInit } from "@angular/core"; -import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { FormBuilder, FormControl, ReactiveFormsModule } from "@angular/forms"; +import { Router } from "@angular/router"; +import { defer, firstValueFrom, map, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { UserId } from "@bitwarden/common/types/guid"; import { AsyncActionsModule, ButtonModule, CheckboxModule, FormFieldModule, + ToastService, } from "@bitwarden/components"; @Component({ @@ -24,15 +32,75 @@ import { ], }) export class LoginDecryptionOptionsComponent implements OnInit { + private activeAccountId: UserId; + private email: string; + loading = false; + // Remember device means for the user to trust the device formGroup = this.formBuilder.group({ - rememberDevice: [true], + rememberDevice: [false], // TODO-rr-bw: change to true by default after testing }); - constructor(private formBuilder: FormBuilder) {} + get rememberDeviceControl(): FormControl { + return this.formGroup.controls.rememberDevice; + } + + constructor( + private accountService: AccountService, + private deviceTrustService: DeviceTrustServiceAbstraction, + private formBuilder: FormBuilder, + private i18nService: I18nService, + private router: Router, + private toastService: ToastService, + ) {} + + async ngOnInit(): Promise { + // this.loading = true; // TODO-rr-bw: uncomment after testing + + this.activeAccountId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + + this.email = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); + + if (!this.email) { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("userEmailMissing"), + }); + + await this.router.navigate(["/login"]); + return; + } + + this.observeAndPersistRememberDeviceValueChanges(); + + // Persist user choice from state if it exists + await this.setRememberDeviceDefaultValue(); + } + + private observeAndPersistRememberDeviceValueChanges(): void { + this.rememberDeviceControl.valueChanges + .pipe( + switchMap((value) => + defer(() => this.deviceTrustService.setShouldTrustDevice(this.activeAccountId, value)), + ), + takeUntilDestroyed(), + ) + .subscribe(); + } + + private async setRememberDeviceDefaultValue() { + const rememberDeviceFromState = await this.deviceTrustService.getShouldTrustDevice( + this.activeAccountId, + ); + + const rememberDevice = rememberDeviceFromState ?? true; - ngOnInit(): void {} + this.rememberDeviceControl.setValue(rememberDevice); + } submit = () => {}; } From c4fa33395a66f27674c427d041e9678574ecf2aa Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:43:29 -0700 Subject: [PATCH 05/28] use takeUntilDestroyed(this.destroyRef) --- .../login-decryption-options.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index e913076bd11..096e69ffd3d 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; +import { Component, DestroyRef, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, FormControl, ReactiveFormsModule } from "@angular/forms"; import { Router } from "@angular/router"; @@ -48,6 +48,7 @@ export class LoginDecryptionOptionsComponent implements OnInit { constructor( private accountService: AccountService, + private destroyRef: DestroyRef, private deviceTrustService: DeviceTrustServiceAbstraction, private formBuilder: FormBuilder, private i18nService: I18nService, @@ -84,10 +85,10 @@ export class LoginDecryptionOptionsComponent implements OnInit { private observeAndPersistRememberDeviceValueChanges(): void { this.rememberDeviceControl.valueChanges .pipe( + takeUntilDestroyed(this.destroyRef), switchMap((value) => defer(() => this.deviceTrustService.setShouldTrustDevice(this.activeAccountId, value)), ), - takeUntilDestroyed(), ) .subscribe(); } From 335958e096d705726c88d89b960ab87854e2d557 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:31:16 -0700 Subject: [PATCH 06/28] migrate loadUntrustedDeviceData() and requestAdminApproval() methods --- .../login-decryption-options.component.html | 57 ++++++++---- .../login-decryption-options.component.ts | 89 ++++++++++++++++--- 2 files changed, 115 insertions(+), 31 deletions(-) diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index d68c04609fe..cb67d6a0a9e 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -9,26 +9,47 @@ -
- - - {{ "rememberThisDevice" | i18n }} - {{ "uncheckIfPublicDevice" | i18n }} - + + + + + {{ "rememberThisDevice" | i18n }} + {{ "uncheckIfPublicDevice" | i18n }} + -
- +
+ -
{{ "or" | i18n }}
+
{{ "or" | i18n }}
- + - -
+ +
+
diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 096e69ffd3d..3fc55ca5206 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -6,23 +6,43 @@ import { Router } from "@angular/router"; import { defer, firstValueFrom, map, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { + LoginEmailServiceAbstraction, + UserDecryptionOptions, + UserDecryptionOptionsServiceAbstraction, +} from "@bitwarden/auth/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { UserId } from "@bitwarden/common/types/guid"; -import { - AsyncActionsModule, - ButtonModule, - CheckboxModule, - FormFieldModule, - ToastService, -} from "@bitwarden/components"; +import { ButtonModule, CheckboxModule, FormFieldModule, ToastService } from "@bitwarden/components"; + +enum State { + NewUser, + ExistingUserUntrustedDevice, +} + +type NewUserData = { + readonly state: State.NewUser; + readonly organizationId: string; + readonly userEmail: string; +}; + +type ExistingUserUntrustedDeviceData = { + readonly state: State.ExistingUserUntrustedDevice; + readonly showApproveFromOtherDeviceBtn: boolean; + readonly showReqAdminApprovalBtn: boolean; + readonly showApproveWithMasterPasswordBtn: boolean; + readonly userEmail: string; +}; + +type Data = NewUserData | ExistingUserUntrustedDeviceData; @Component({ standalone: true, templateUrl: "./login-decryption-options.component.html", imports: [ - AsyncActionsModule, ButtonModule, CheckboxModule, CommonModule, @@ -35,11 +55,13 @@ export class LoginDecryptionOptionsComponent implements OnInit { private activeAccountId: UserId; private email: string; - loading = false; + protected data?: Data; + protected loading = false; + protected State = State; - // Remember device means for the user to trust the device - formGroup = this.formBuilder.group({ - rememberDevice: [false], // TODO-rr-bw: change to true by default after testing + protected formGroup = this.formBuilder.group({ + // TODO-rr-bw: change to true by default after testing + rememberDevice: [false], // Remember device means for the user to trust the device }); get rememberDeviceControl(): FormControl { @@ -52,8 +74,11 @@ export class LoginDecryptionOptionsComponent implements OnInit { private deviceTrustService: DeviceTrustServiceAbstraction, private formBuilder: FormBuilder, private i18nService: I18nService, + private loginEmailService: LoginEmailServiceAbstraction, private router: Router, private toastService: ToastService, + private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, + private validationService: ValidationService, ) {} async ngOnInit(): Promise { @@ -80,6 +105,23 @@ export class LoginDecryptionOptionsComponent implements OnInit { // Persist user choice from state if it exists await this.setRememberDeviceDefaultValue(); + + try { + const userDecryptionOptions = await firstValueFrom( + this.userDecryptionOptionsService.userDecryptionOptions$, + ); + + if ( + !userDecryptionOptions?.trustedDeviceOption?.hasAdminApproval && + !userDecryptionOptions?.hasMasterPassword + ) { + // this.loadNewUserData(); + } else { + this.loadUntrustedDeviceData(userDecryptionOptions); + } + } catch (err) { + this.validationService.showError(err); + } } private observeAndPersistRememberDeviceValueChanges(): void { @@ -103,5 +145,26 @@ export class LoginDecryptionOptionsComponent implements OnInit { this.rememberDeviceControl.setValue(rememberDevice); } - submit = () => {}; + private loadUntrustedDeviceData(userDecryptionOptions: UserDecryptionOptions) { + const showApproveFromOtherDeviceBtn = + userDecryptionOptions?.trustedDeviceOption?.hasLoginApprovingDevice || false; + + const showReqAdminApprovalBtn = + !!userDecryptionOptions?.trustedDeviceOption?.hasAdminApproval || false; + + const showApproveWithMasterPasswordBtn = userDecryptionOptions?.hasMasterPassword || false; + + this.data = { + state: State.ExistingUserUntrustedDevice, + showApproveFromOtherDeviceBtn, + showReqAdminApprovalBtn, + showApproveWithMasterPasswordBtn, + userEmail: this.email, + }; + } + + protected async requestAdminApproval() { + this.loginEmailService.setLoginEmail(this.data.userEmail); + await this.router.navigate(["/admin-approval-requested"]); + } } From d5660a9a3d855a4d8193733cddf5209ce5c568d1 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:36:43 -0700 Subject: [PATCH 07/28] migrate approveFromOtherDevice() method --- .../login-decryption-options.component.html | 1 + .../login-decryption-options.component.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index cb67d6a0a9e..67ec9f61da6 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -24,6 +24,7 @@ bitButton block buttonType="primary" + (click)="approveFromOtherDevice()" > {{ "approveFromYourOtherDevice" | i18n }} diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 3fc55ca5206..719d1af37d9 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -163,6 +163,15 @@ export class LoginDecryptionOptionsComponent implements OnInit { }; } + protected async approveFromOtherDevice() { + if (this.data.state !== State.ExistingUserUntrustedDevice) { + return; + } + + this.loginEmailService.setLoginEmail(this.data.userEmail); + await this.router.navigate(["/login-with-device"]); + } + protected async requestAdminApproval() { this.loginEmailService.setLoginEmail(this.data.userEmail); await this.router.navigate(["/admin-approval-requested"]); From a2c3735caa3f3fe1f2fa52d6a178fe4c37b8897e Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:38:25 -0700 Subject: [PATCH 08/28] migrate approveWithMasterPassword() method --- .../login-decryption-options.component.html | 1 + .../login-decryption-options.component.ts | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index 67ec9f61da6..82cd2426832 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -37,6 +37,7 @@ bitButton block buttonType="secondary" + (click)="approveWithMasterPassword()" > {{ "useMasterPassword" | i18n }} diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 719d1af37d9..c0b874e1511 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -172,6 +172,14 @@ export class LoginDecryptionOptionsComponent implements OnInit { await this.router.navigate(["/login-with-device"]); } + protected async approveWithMasterPassword() { + await this.router.navigate(["/lock"], { + queryParams: { + from: "login-initiated", + }, + }); + } + protected async requestAdminApproval() { this.loginEmailService.setLoginEmail(this.data.userEmail); await this.router.navigate(["/admin-approval-requested"]); From 6aa416ea665d4eab271543112700fd7ab06f3f54 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:24:42 -0700 Subject: [PATCH 09/28] migrate loadNewUserData() and restructure template --- .../login-decryption-options.component.html | 88 ++++++++++--------- .../login-decryption-options.component.ts | 59 +++++++++++-- 2 files changed, 101 insertions(+), 46 deletions(-) diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index 82cd2426832..385b96a96c5 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -10,48 +10,54 @@
- - - - {{ "rememberThisDevice" | i18n }} - {{ "uncheckIfPublicDevice" | i18n }} - + + + {{ "rememberThisDevice" | i18n }} + {{ "uncheckIfPublicDevice" | i18n }} + + -
- + + + -
{{ "or" | i18n }}
+ +
+ - +
{{ "or" | i18n }}
- -
-
- + + + +
+
diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index c0b874e1511..6dff069a8e1 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -3,7 +3,7 @@ import { Component, DestroyRef, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, FormControl, ReactiveFormsModule } from "@angular/forms"; import { Router } from "@angular/router"; -import { defer, firstValueFrom, map, switchMap } from "rxjs"; +import { catchError, defer, firstValueFrom, from, map, of, switchMap, throwError } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { @@ -11,12 +11,21 @@ import { UserDecryptionOptions, UserDecryptionOptionsServiceAbstraction, } from "@bitwarden/auth/common"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { UserId } from "@bitwarden/common/types/guid"; -import { ButtonModule, CheckboxModule, FormFieldModule, ToastService } from "@bitwarden/components"; +import { + AsyncActionsModule, + ButtonModule, + CheckboxModule, + FormFieldModule, + ToastService, + TypographyModule, +} from "@bitwarden/components"; enum State { NewUser, @@ -43,12 +52,14 @@ type Data = NewUserData | ExistingUserUntrustedDeviceData; standalone: true, templateUrl: "./login-decryption-options.component.html", imports: [ + AsyncActionsModule, ButtonModule, CheckboxModule, CommonModule, FormFieldModule, JslibModule, ReactiveFormsModule, + TypographyModule, ], }) export class LoginDecryptionOptionsComponent implements OnInit { @@ -75,13 +86,15 @@ export class LoginDecryptionOptionsComponent implements OnInit { private formBuilder: FormBuilder, private i18nService: I18nService, private loginEmailService: LoginEmailServiceAbstraction, + private organizationApiService: OrganizationApiServiceAbstraction, private router: Router, + private ssoLoginService: SsoLoginServiceAbstraction, private toastService: ToastService, private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, private validationService: ValidationService, ) {} - async ngOnInit(): Promise { + async ngOnInit() { // this.loading = true; // TODO-rr-bw: uncomment after testing this.activeAccountId = (await firstValueFrom(this.accountService.activeAccount$))?.id; @@ -115,7 +128,7 @@ export class LoginDecryptionOptionsComponent implements OnInit { !userDecryptionOptions?.trustedDeviceOption?.hasAdminApproval && !userDecryptionOptions?.hasMasterPassword ) { - // this.loadNewUserData(); + await this.loadNewUserData(); } else { this.loadUntrustedDeviceData(userDecryptionOptions); } @@ -124,7 +137,7 @@ export class LoginDecryptionOptionsComponent implements OnInit { } } - private observeAndPersistRememberDeviceValueChanges(): void { + private observeAndPersistRememberDeviceValueChanges() { this.rememberDeviceControl.valueChanges .pipe( takeUntilDestroyed(this.destroyRef), @@ -145,7 +158,37 @@ export class LoginDecryptionOptionsComponent implements OnInit { this.rememberDeviceControl.setValue(rememberDevice); } + private async loadNewUserData() { + const autoEnrollStatus$ = defer(() => + this.ssoLoginService.getActiveUserOrganizationSsoIdentifier(), + ).pipe( + switchMap((organizationIdentifier) => { + if (organizationIdentifier == undefined) { + return throwError(() => new Error(this.i18nService.t("ssoIdentifierRequired"))); + } + + return from(this.organizationApiService.getAutoEnrollStatus(organizationIdentifier)); + }), + catchError((err: unknown) => { + this.validationService.showError(err); + return of(undefined); + }), + ); + + const autoEnrollStatus = await firstValueFrom(autoEnrollStatus$); + + this.data = { + state: State.NewUser, + organizationId: autoEnrollStatus.id, + userEmail: this.email, + }; + + this.loading = false; + } + private loadUntrustedDeviceData(userDecryptionOptions: UserDecryptionOptions) { + this.loading = true; + const showApproveFromOtherDeviceBtn = userDecryptionOptions?.trustedDeviceOption?.hasLoginApprovingDevice || false; @@ -161,6 +204,12 @@ export class LoginDecryptionOptionsComponent implements OnInit { showApproveWithMasterPasswordBtn, userEmail: this.email, }; + + this.loading = false; + } + + protected async createUserAction() { + // TODO-rr-bw: implement } protected async approveFromOtherDevice() { From b7b613bfbbf1fc6191e7122082405eb6a89ed638 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:12:27 -0700 Subject: [PATCH 10/28] setup services --- apps/web/src/app/auth/core/services/index.ts | 1 + .../auth/core/services/login-decryption-options/index.ts | 1 + .../web-login-decryption-options.service.ts | 8 ++++++++ apps/web/src/app/core/core.module.ts | 7 +++++++ libs/angular/src/services/jslib-services.module.ts | 7 +++++++ libs/auth/src/angular/index.ts | 2 ++ .../default-login-decryption-options.service.ts | 3 +++ .../login-decryption-options.service.ts | 1 + 8 files changed, 30 insertions(+) create mode 100644 apps/web/src/app/auth/core/services/login-decryption-options/index.ts create mode 100644 apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts create mode 100644 libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts create mode 100644 libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts diff --git a/apps/web/src/app/auth/core/services/index.ts b/apps/web/src/app/auth/core/services/index.ts index a2e674c2a95..c14292d7c6d 100644 --- a/apps/web/src/app/auth/core/services/index.ts +++ b/apps/web/src/app/auth/core/services/index.ts @@ -1,4 +1,5 @@ export * from "./login"; +export * from "./login-decryption-options"; export * from "./webauthn-login"; export * from "./set-password-jit"; export * from "./registration"; diff --git a/apps/web/src/app/auth/core/services/login-decryption-options/index.ts b/apps/web/src/app/auth/core/services/login-decryption-options/index.ts new file mode 100644 index 00000000000..f0ff30b8727 --- /dev/null +++ b/apps/web/src/app/auth/core/services/login-decryption-options/index.ts @@ -0,0 +1 @@ +export * from "./web-login-decryption-options.service"; diff --git a/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts new file mode 100644 index 00000000000..51ad0c633c1 --- /dev/null +++ b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts @@ -0,0 +1,8 @@ +import { + LoginDecryptionOptionsService, + DefaultLoginDecryptionOptionsService, +} from "@bitwarden/auth/angular"; + +export class WebLoginDecryptionOptionsService + extends DefaultLoginDecryptionOptionsService + implements LoginDecryptionOptionsService {} diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index d1fa3f8ba8c..5b14ccb0406 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -30,6 +30,7 @@ import { LoginComponentService, LockComponentService, SetPasswordJitService, + LoginDecryptionOptionsService, } from "@bitwarden/auth/angular"; import { InternalUserDecryptionOptionsServiceAbstraction, @@ -91,6 +92,7 @@ import { WebRegistrationFinishService, WebLoginComponentService, WebLockComponentService, + WebLoginDecryptionOptionsService, } from "../auth"; import { AcceptOrganizationInviteService } from "../auth/organization-invite/accept-organization.service"; import { HtmlStorageService } from "../core/html-storage.service"; @@ -286,6 +288,11 @@ const safeProviders: SafeProvider[] = [ useClass: LoginEmailService, deps: [AccountService, AuthService, StateProvider], }), + safeProvider({ + provide: LoginDecryptionOptionsService, + useClass: WebLoginDecryptionOptionsService, + deps: [], + }), ]; @NgModule({ diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 5d8822866d6..09aa209c9b4 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -16,6 +16,8 @@ import { DefaultAnonLayoutWrapperDataService, LoginComponentService, DefaultLoginComponentService, + LoginDecryptionOptionsService, + DefaultLoginDecryptionOptionsService, } from "@bitwarden/auth/angular"; import { AuthRequestServiceAbstraction, @@ -1365,6 +1367,11 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultCipherAuthorizationService, deps: [CollectionService, OrganizationServiceAbstraction], }), + safeProvider({ + provide: LoginDecryptionOptionsService, + useClass: DefaultLoginDecryptionOptionsService, + deps: [], + }), ]; @NgModule({ diff --git a/libs/auth/src/angular/index.ts b/libs/auth/src/angular/index.ts index 0deb9327869..39ae8be9e64 100644 --- a/libs/auth/src/angular/index.ts +++ b/libs/auth/src/angular/index.ts @@ -26,6 +26,8 @@ export * from "./login/default-login-component.service"; // login-decryption-options export * from "./login-decryption-options/login-decryption-options.component"; +export * from "./login-decryption-options/login-decryption-options.service"; +export * from "./login-decryption-options/default-login-decryption-options.service"; // password callout export * from "./password-callout/password-callout.component"; diff --git a/libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts b/libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts new file mode 100644 index 00000000000..5d8d28a5c60 --- /dev/null +++ b/libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts @@ -0,0 +1,3 @@ +import { LoginDecryptionOptionsService } from "./login-decryption-options.service"; + +export class DefaultLoginDecryptionOptionsService implements LoginDecryptionOptionsService {} diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts new file mode 100644 index 00000000000..964da416b34 --- /dev/null +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts @@ -0,0 +1 @@ +export abstract class LoginDecryptionOptionsService {} From 869add9d3c32cab514da7b5e99bf52c8806cbd21 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:47:08 -0700 Subject: [PATCH 11/28] migrate and refactor createUser() method --- .../web-login-decryption-options.service.ts | 27 +++++++- ...efault-login-decryption-options.service.ts | 6 +- .../login-decryption-options.component.html | 2 +- .../login-decryption-options.component.ts | 63 ++++++++++++++++++- .../login-decryption-options.service.ts | 4 +- 5 files changed, 95 insertions(+), 7 deletions(-) diff --git a/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts index 51ad0c633c1..f45dbdff3c4 100644 --- a/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts +++ b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts @@ -1,8 +1,33 @@ +import { inject } from "@angular/core"; + import { LoginDecryptionOptionsService, DefaultLoginDecryptionOptionsService, } from "@bitwarden/auth/angular"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; + +import { RouterService } from "../../../../core/router.service"; +import { AcceptOrganizationInviteService } from "../../../organization-invite/accept-organization.service"; export class WebLoginDecryptionOptionsService extends DefaultLoginDecryptionOptionsService - implements LoginDecryptionOptionsService {} + implements LoginDecryptionOptionsService +{ + protected routerService = inject(RouterService); + protected acceptOrganizationInviteService = inject(AcceptOrganizationInviteService); + protected validationService = inject(ValidationService); + + override async handleCreateUserSuccess(): Promise { + try { + // Invites from TDE orgs go through here, but the invite is + // accepted while being enrolled in admin recovery. So we need to clear + // the redirect and stored org invite. + await this.routerService.getAndClearLoginRedirectUrl(); + await this.acceptOrganizationInviteService.clearOrganizationInvitation(); + + // await this.router.navigate(["/vault"]); // TODO-rr-bw: move routing to component? + } catch (error) { + this.validationService.showError(error); + } + } +} diff --git a/libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts b/libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts index 5d8d28a5c60..fa4453adb78 100644 --- a/libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts +++ b/libs/auth/src/angular/login-decryption-options/default-login-decryption-options.service.ts @@ -1,3 +1,7 @@ import { LoginDecryptionOptionsService } from "./login-decryption-options.service"; -export class DefaultLoginDecryptionOptionsService implements LoginDecryptionOptionsService {} +export class DefaultLoginDecryptionOptionsService implements LoginDecryptionOptionsService { + handleCreateUserSuccess(): Promise { + return null; + } +} diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index 385b96a96c5..58094228b0e 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -18,7 +18,7 @@ - diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 6dff069a8e1..ec7a978a041 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -11,11 +11,17 @@ import { UserDecryptionOptions, UserDecryptionOptionsServiceAbstraction, } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { ClientType } from "@bitwarden/common/enums"; +import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.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 { @@ -26,6 +32,9 @@ import { ToastService, TypographyModule, } from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; + +import { LoginDecryptionOptionsService } from "./login-decryption-options.service"; enum State { NewUser, @@ -64,6 +73,7 @@ type Data = NewUserData | ExistingUserUntrustedDeviceData; }) export class LoginDecryptionOptionsComponent implements OnInit { private activeAccountId: UserId; + private clientType: ClientType; private email: string; protected data?: Data; @@ -81,18 +91,26 @@ export class LoginDecryptionOptionsComponent implements OnInit { constructor( private accountService: AccountService, + private apiService: ApiService, private destroyRef: DestroyRef, private deviceTrustService: DeviceTrustServiceAbstraction, private formBuilder: FormBuilder, private i18nService: I18nService, + private keyService: KeyService, + private loginDecryptionOptionsService: LoginDecryptionOptionsService, private loginEmailService: LoginEmailServiceAbstraction, + private messagingService: MessagingService, private organizationApiService: OrganizationApiServiceAbstraction, + private passwordResetEnrollmentService: PasswordResetEnrollmentServiceAbstraction, + private platformUtilsService: PlatformUtilsService, private router: Router, private ssoLoginService: SsoLoginServiceAbstraction, private toastService: ToastService, private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, private validationService: ValidationService, - ) {} + ) { + this.clientType === this.platformUtilsService.getClientType(); + } async ngOnInit() { // this.loading = true; // TODO-rr-bw: uncomment after testing @@ -208,8 +226,47 @@ export class LoginDecryptionOptionsComponent implements OnInit { this.loading = false; } - protected async createUserAction() { - // TODO-rr-bw: implement + async createUser() { + if (this.data.state !== State.NewUser) { + return; + } + + // this.loading to support clients without async-actions-support + this.loading = true; + // errors must be caught in child components to prevent navigation + try { + const { publicKey, privateKey } = await this.keyService.initAccount(); + const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString); + await this.apiService.postAccountKeys(keysRequest); + + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("accountSuccessfullyCreated"), + }); + + await this.passwordResetEnrollmentService.enroll(this.data.organizationId); + + if (this.formGroup.value.rememberDevice) { + await this.deviceTrustService.trustDevice(this.activeAccountId); + } + + await this.loginDecryptionOptionsService.handleCreateUserSuccess(); + } catch (err) { + this.validationService.showError(err); + } finally { + this.loading = false; + } + + if (this.clientType === ClientType.Desktop) { + this.messagingService.send("redrawMenu"); + } + + if (this.clientType === ClientType.Browser) { + await this.router.navigate(["/tabs/vault"]); + } else { + await this.router.navigate(["/vault"]); + } } protected async approveFromOtherDevice() { diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts index 964da416b34..7b1a2b19401 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.service.ts @@ -1 +1,3 @@ -export abstract class LoginDecryptionOptionsService {} +export abstract class LoginDecryptionOptionsService { + abstract handleCreateUserSuccess(): Promise; +} From 7f0ff35bcdd649ea66e22a395647753d9848546a Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:52:18 -0700 Subject: [PATCH 12/28] remove comment --- .../web-login-decryption-options.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts index f45dbdff3c4..b9ea5724422 100644 --- a/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts +++ b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts @@ -24,8 +24,6 @@ export class WebLoginDecryptionOptionsService // the redirect and stored org invite. await this.routerService.getAndClearLoginRedirectUrl(); await this.acceptOrganizationInviteService.clearOrganizationInvitation(); - - // await this.router.navigate(["/vault"]); // TODO-rr-bw: move routing to component? } catch (error) { this.validationService.showError(error); } From ab66de10c328547f1ac2c8a5edd0904b4eaead69 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Sat, 2 Nov 2024 15:58:03 -0700 Subject: [PATCH 13/28] refactor email usage and loading state --- .../web-login-decryption-options.service.ts | 2 +- .../login-decryption-options.component.html | 2 +- .../login-decryption-options.component.ts | 35 ++++++++----------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts index b9ea5724422..16950e383be 100644 --- a/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts +++ b/apps/web/src/app/auth/core/services/login-decryption-options/web-login-decryption-options.service.ts @@ -25,7 +25,7 @@ export class WebLoginDecryptionOptionsService await this.routerService.getAndClearLoginRedirectUrl(); await this.acceptOrganizationInviteService.clearOrganizationInvitation(); } catch (error) { - this.validationService.showError(error); + this.validationService.showError(error); // TODO-rr-bw: rethrow error? } } } diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index 58094228b0e..4bc48abffc9 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -50,7 +50,7 @@ - +
+ + -
{{ "or" | i18n }}
+
+ {{ "or" | i18n }} +
+