mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
[PM-8116] Auth Browser Refresh: Password Hint Component (#10492)
* setup component, services, and web HTML * make Web and Browser functional * make desktop functional * update template to solidify common client HTML * simplify template and class * update browser routing * move canActivate to correct location * simplify post submit routing * update routing to use unauthUiRefreshSwap() * constrain AnonLayout title/subtitle width, reduce height on destkop to account for header * reduce height on browser to account for header (otherwise have to scroll to see EnvSelector * resolve email issue when clicking 'cancel' on extension popout * update routing for web * persist email to popout * update web router and anon-layout min-h based on client * change anchor link to button * remove unnecessary formatting changes * add new icon * remove unnecessary call to loginEmailService
This commit is contained in:
@@ -179,6 +179,18 @@
|
|||||||
"addItem": {
|
"addItem": {
|
||||||
"message": "Add item"
|
"message": "Add item"
|
||||||
},
|
},
|
||||||
|
"accountEmail": {
|
||||||
|
"message": "Account email"
|
||||||
|
},
|
||||||
|
"requestHint": {
|
||||||
|
"message": "Request hint"
|
||||||
|
},
|
||||||
|
"requestPasswordHint": {
|
||||||
|
"message": "Request password hint"
|
||||||
|
},
|
||||||
|
"enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": {
|
||||||
|
"message": "Enter your account email address and your password hint will be sent to you"
|
||||||
|
},
|
||||||
"passwordHint": {
|
"passwordHint": {
|
||||||
"message": "Password hint"
|
"message": "Password hint"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Injectable, NgModule } from "@angular/core";
|
import { Injectable, NgModule } from "@angular/core";
|
||||||
import { ActivatedRouteSnapshot, RouteReuseStrategy, RouterModule, Routes } from "@angular/router";
|
import { ActivatedRouteSnapshot, RouteReuseStrategy, RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
|
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
|
||||||
|
import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap";
|
||||||
import {
|
import {
|
||||||
authGuard,
|
authGuard,
|
||||||
lockGuard,
|
lockGuard,
|
||||||
@@ -15,11 +17,13 @@ import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh
|
|||||||
import {
|
import {
|
||||||
AnonLayoutWrapperComponent,
|
AnonLayoutWrapperComponent,
|
||||||
AnonLayoutWrapperData,
|
AnonLayoutWrapperData,
|
||||||
|
PasswordHintComponent,
|
||||||
RegistrationFinishComponent,
|
RegistrationFinishComponent,
|
||||||
RegistrationStartComponent,
|
RegistrationStartComponent,
|
||||||
RegistrationStartSecondaryComponent,
|
RegistrationStartSecondaryComponent,
|
||||||
RegistrationStartSecondaryComponentData,
|
RegistrationStartSecondaryComponentData,
|
||||||
SetPasswordJitComponent,
|
SetPasswordJitComponent,
|
||||||
|
UserLockIcon,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
|
||||||
@@ -27,6 +31,7 @@ import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-fa
|
|||||||
import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard";
|
import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard";
|
||||||
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
||||||
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
||||||
|
import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component";
|
||||||
import { HintComponent } from "../auth/popup/hint.component";
|
import { HintComponent } from "../auth/popup/hint.component";
|
||||||
import { HomeComponent } from "../auth/popup/home.component";
|
import { HomeComponent } from "../auth/popup/home.component";
|
||||||
import { LockComponent } from "../auth/popup/lock.component";
|
import { LockComponent } from "../auth/popup/lock.component";
|
||||||
@@ -213,12 +218,6 @@ const routes: Routes = [
|
|||||||
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||||
data: { state: "register" },
|
data: { state: "register" },
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "hint",
|
|
||||||
component: HintComponent,
|
|
||||||
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
|
||||||
data: { state: "hint" },
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "environment",
|
path: "environment",
|
||||||
component: EnvironmentComponent,
|
component: EnvironmentComponent,
|
||||||
@@ -385,6 +384,41 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { state: "update-temp-password" },
|
data: { state: "update-temp-password" },
|
||||||
},
|
},
|
||||||
|
...unauthUiRefreshSwap(
|
||||||
|
HintComponent,
|
||||||
|
ExtensionAnonLayoutWrapperComponent,
|
||||||
|
{
|
||||||
|
path: "hint",
|
||||||
|
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||||
|
data: {
|
||||||
|
state: "hint",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "hint",
|
||||||
|
canActivate: [unauthGuardFn(unauthRouteOverrides)],
|
||||||
|
data: {
|
||||||
|
pageTitle: "requestPasswordHint",
|
||||||
|
pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
|
||||||
|
pageIcon: UserLockIcon,
|
||||||
|
showBackButton: true,
|
||||||
|
state: "hint",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{ path: "", component: PasswordHintComponent },
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: EnvironmentSelectorComponent,
|
||||||
|
outlet: "environment-selector",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
component: AnonLayoutWrapperComponent,
|
component: AnonLayoutWrapperComponent,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { AvatarModule, ButtonModule, ToastModule } from "@bitwarden/components";
|
|||||||
import { AccountComponent } from "../auth/popup/account-switching/account.component";
|
import { AccountComponent } from "../auth/popup/account-switching/account.component";
|
||||||
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
|
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
|
||||||
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
import { EnvironmentComponent } from "../auth/popup/environment.component";
|
||||||
|
import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component";
|
||||||
import { HintComponent } from "../auth/popup/hint.component";
|
import { HintComponent } from "../auth/popup/hint.component";
|
||||||
import { HomeComponent } from "../auth/popup/home.component";
|
import { HomeComponent } from "../auth/popup/home.component";
|
||||||
import { LockComponent } from "../auth/popup/lock.component";
|
import { LockComponent } from "../auth/popup/lock.component";
|
||||||
@@ -131,6 +132,7 @@ import "../platform/popup/locales";
|
|||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
UserVerificationDialogComponent,
|
UserVerificationDialogComponent,
|
||||||
CurrentAccountComponent,
|
CurrentAccountComponent,
|
||||||
|
ExtensionAnonLayoutWrapperComponent,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
ActionButtonsComponent,
|
ActionButtonsComponent,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
|
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
|
||||||
|
import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap";
|
||||||
import {
|
import {
|
||||||
authGuard,
|
authGuard,
|
||||||
lockGuard,
|
lockGuard,
|
||||||
@@ -12,11 +14,13 @@ import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag
|
|||||||
import {
|
import {
|
||||||
AnonLayoutWrapperComponent,
|
AnonLayoutWrapperComponent,
|
||||||
AnonLayoutWrapperData,
|
AnonLayoutWrapperData,
|
||||||
|
PasswordHintComponent,
|
||||||
RegistrationFinishComponent,
|
RegistrationFinishComponent,
|
||||||
RegistrationStartComponent,
|
RegistrationStartComponent,
|
||||||
RegistrationStartSecondaryComponent,
|
RegistrationStartSecondaryComponent,
|
||||||
RegistrationStartSecondaryComponentData,
|
RegistrationStartSecondaryComponentData,
|
||||||
SetPasswordJitComponent,
|
SetPasswordJitComponent,
|
||||||
|
UserLockIcon,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
|
||||||
@@ -94,7 +98,6 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
{ path: "accessibility-cookie", component: AccessibilityCookieComponent },
|
{ path: "accessibility-cookie", component: AccessibilityCookieComponent },
|
||||||
{ path: "hint", component: HintComponent },
|
|
||||||
{ path: "set-password", component: SetPasswordComponent },
|
{ path: "set-password", component: SetPasswordComponent },
|
||||||
{ path: "sso", component: SsoComponent },
|
{ path: "sso", component: SsoComponent },
|
||||||
{
|
{
|
||||||
@@ -113,10 +116,53 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { titleId: "removeMasterPassword" },
|
data: { titleId: "removeMasterPassword" },
|
||||||
},
|
},
|
||||||
|
...unauthUiRefreshSwap(
|
||||||
|
HintComponent,
|
||||||
|
AnonLayoutWrapperComponent,
|
||||||
|
{
|
||||||
|
path: "hint",
|
||||||
|
canActivate: [unauthGuardFn()],
|
||||||
|
data: {
|
||||||
|
pageTitle: "passwordHint",
|
||||||
|
titleId: "passwordHint",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "hint",
|
||||||
|
canActivate: [unauthGuardFn()],
|
||||||
|
data: {
|
||||||
|
pageTitle: "requestPasswordHint",
|
||||||
|
pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
|
||||||
|
pageIcon: UserLockIcon,
|
||||||
|
state: "hint",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{ path: "", component: PasswordHintComponent },
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: EnvironmentSelectorComponent,
|
||||||
|
outlet: "environment-selector",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
component: AnonLayoutWrapperComponent,
|
component: AnonLayoutWrapperComponent,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: "hint",
|
||||||
|
component: PasswordHintComponent,
|
||||||
|
data: {
|
||||||
|
pageTitle: "requestPasswordHint",
|
||||||
|
pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "signup",
|
path: "signup",
|
||||||
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
|
||||||
|
|||||||
@@ -560,6 +560,18 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"message": "Settings"
|
"message": "Settings"
|
||||||
},
|
},
|
||||||
|
"accountEmail": {
|
||||||
|
"message": "Account email"
|
||||||
|
},
|
||||||
|
"requestHint": {
|
||||||
|
"message": "Request hint"
|
||||||
|
},
|
||||||
|
"requestPasswordHint": {
|
||||||
|
"message": "Request password hint"
|
||||||
|
},
|
||||||
|
"enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": {
|
||||||
|
"message": "Enter your account email address and your password hint will be sent to you"
|
||||||
|
},
|
||||||
"passwordHint": {
|
"passwordHint": {
|
||||||
"message": "Password hint"
|
"message": "Password hint"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
import { Route, RouterModule, Routes } from "@angular/router";
|
import { Route, RouterModule, Routes } from "@angular/router";
|
||||||
|
|
||||||
|
import { unauthUiRefreshSwap } from "@bitwarden/angular/auth/functions/unauth-ui-refresh-route-swap";
|
||||||
import {
|
import {
|
||||||
authGuard,
|
authGuard,
|
||||||
lockGuard,
|
lockGuard,
|
||||||
@@ -12,13 +13,15 @@ import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag
|
|||||||
import {
|
import {
|
||||||
AnonLayoutWrapperComponent,
|
AnonLayoutWrapperComponent,
|
||||||
AnonLayoutWrapperData,
|
AnonLayoutWrapperData,
|
||||||
|
PasswordHintComponent,
|
||||||
RegistrationFinishComponent,
|
RegistrationFinishComponent,
|
||||||
RegistrationStartComponent,
|
RegistrationStartComponent,
|
||||||
RegistrationStartSecondaryComponent,
|
RegistrationStartSecondaryComponent,
|
||||||
RegistrationStartSecondaryComponentData,
|
RegistrationStartSecondaryComponentData,
|
||||||
SetPasswordJitComponent,
|
SetPasswordJitComponent,
|
||||||
LockIcon,
|
|
||||||
RegistrationLinkExpiredComponent,
|
RegistrationLinkExpiredComponent,
|
||||||
|
LockIcon,
|
||||||
|
UserLockIcon,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
|
||||||
@@ -167,6 +170,49 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
...unauthUiRefreshSwap(
|
||||||
|
AnonLayoutWrapperComponent,
|
||||||
|
AnonLayoutWrapperComponent,
|
||||||
|
{
|
||||||
|
path: "hint",
|
||||||
|
canActivate: [unauthGuardFn()],
|
||||||
|
data: {
|
||||||
|
pageTitle: "passwordHint",
|
||||||
|
titleId: "passwordHint",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{ path: "", component: HintComponent },
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: EnvironmentSelectorComponent,
|
||||||
|
outlet: "environment-selector",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "hint",
|
||||||
|
canActivate: [unauthGuardFn()],
|
||||||
|
data: {
|
||||||
|
pageTitle: "requestPasswordHint",
|
||||||
|
pageSubtitle: "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou",
|
||||||
|
pageIcon: UserLockIcon,
|
||||||
|
state: "hint",
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{ path: "", component: PasswordHintComponent },
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: EnvironmentSelectorComponent,
|
||||||
|
outlet: "environment-selector",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
{
|
{
|
||||||
path: "",
|
path: "",
|
||||||
component: AnonLayoutWrapperComponent,
|
component: AnonLayoutWrapperComponent,
|
||||||
@@ -388,25 +434,6 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "hint",
|
|
||||||
canActivate: [unauthGuardFn()],
|
|
||||||
data: {
|
|
||||||
pageTitle: "passwordHint",
|
|
||||||
titleId: "passwordHint",
|
|
||||||
} satisfies DataProperties & AnonLayoutWrapperData,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
component: HintComponent,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
component: EnvironmentSelectorComponent,
|
|
||||||
outlet: "environment-selector",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "remove-password",
|
path: "remove-password",
|
||||||
component: RemovePasswordComponent,
|
component: RemovePasswordComponent,
|
||||||
|
|||||||
@@ -960,6 +960,18 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"message": "Settings"
|
"message": "Settings"
|
||||||
},
|
},
|
||||||
|
"accountEmail": {
|
||||||
|
"message": "Account email"
|
||||||
|
},
|
||||||
|
"requestHint": {
|
||||||
|
"message": "Request hint"
|
||||||
|
},
|
||||||
|
"requestPasswordHint": {
|
||||||
|
"message": "Request password hint"
|
||||||
|
},
|
||||||
|
"enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": {
|
||||||
|
"message": "Enter your account email address and your password hint will be sent to you"
|
||||||
|
},
|
||||||
"passwordHint": {
|
"passwordHint": {
|
||||||
"message": "Password hint"
|
"message": "Password hint"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -317,6 +317,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit,
|
|||||||
// Try to load from memory first
|
// Try to load from memory first
|
||||||
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
||||||
const rememberEmail = this.loginEmailService.getRememberEmail();
|
const rememberEmail = this.loginEmailService.getRememberEmail();
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
this.formGroup.controls.email.setValue(email);
|
this.formGroup.controls.email.setValue(email);
|
||||||
this.formGroup.controls.rememberEmail.setValue(rememberEmail);
|
this.formGroup.controls.rememberEmail.setValue(rememberEmail);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function unauthUiRefreshSwap(
|
|||||||
defaultComponent: Type<any>,
|
defaultComponent: Type<any>,
|
||||||
refreshedComponent: Type<any>,
|
refreshedComponent: Type<any>,
|
||||||
options: Route,
|
options: Route,
|
||||||
|
altOptions?: Route,
|
||||||
): Routes {
|
): Routes {
|
||||||
return componentRouteSwap(
|
return componentRouteSwap(
|
||||||
defaultComponent,
|
defaultComponent,
|
||||||
@@ -29,5 +30,6 @@ export function unauthUiRefreshSwap(
|
|||||||
return configService.getFeatureFlag(FeatureFlag.UnauthenticatedExtensionUIRefresh);
|
return configService.getFeatureFlag(FeatureFlag.UnauthenticatedExtensionUIRefresh);
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
|
altOptions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
<!-- The calc() reductions are to account for browser/desktop headers -->
|
||||||
<main
|
<main
|
||||||
class="tw-flex tw-min-h-screen tw-w-full tw-mx-auto tw-flex-col tw-gap-7 tw-bg-background-alt tw-px-8 tw-pb-4 tw-text-main"
|
class="tw-flex tw-w-full tw-mx-auto tw-flex-col tw-gap-7 tw-bg-background-alt tw-px-8 tw-pb-4 tw-text-main"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'tw-pt-0': decreaseTopPadding,
|
'tw-pt-0': decreaseTopPadding,
|
||||||
'tw-pt-8': !decreaseTopPadding,
|
'tw-pt-8': !decreaseTopPadding,
|
||||||
'tw-relative tw-top-0': clientType === 'browser',
|
'tw-min-h-screen': clientType === 'web',
|
||||||
|
'tw-min-h-[calc(100vh-72px)]': clientType === 'browser',
|
||||||
|
'tw-min-h-[calc(100vh-54px)]': clientType === 'desktop',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<bit-icon *ngIf="!hideLogo" [icon]="logo" class="tw-w-[128px] [&>*]:tw-align-top"></bit-icon>
|
<bit-icon *ngIf="!hideLogo" [icon]="logo" class="tw-w-[128px] [&>*]:tw-align-top"></bit-icon>
|
||||||
@@ -23,6 +26,7 @@
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</h1>
|
</h1>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div *ngIf="subtitle" class="tw-text-sm sm:tw-text-base">{{ subtitle }}</div>
|
<div *ngIf="subtitle" class="tw-text-sm sm:tw-text-base">{{ subtitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from "./bitwarden-logo.icon";
|
export * from "./bitwarden-logo.icon";
|
||||||
export * from "./bitwarden-shield.icon";
|
export * from "./bitwarden-shield.icon";
|
||||||
export * from "./lock.icon";
|
export * from "./lock.icon";
|
||||||
|
export * from "./user-lock.icon";
|
||||||
export * from "./user-verification-biometrics-fingerprint.icon";
|
export * from "./user-verification-biometrics-fingerprint.icon";
|
||||||
|
|||||||
22
libs/auth/src/angular/icons/user-lock.icon.ts
Normal file
22
libs/auth/src/angular/icons/user-lock.icon.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { svgIcon } from "@bitwarden/components";
|
||||||
|
|
||||||
|
export const UserLockIcon = svgIcon`
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="100" fill="none">
|
||||||
|
<path class="tw-fill-text-headers" fill-rule="evenodd" d="M0 18.207a7.798 7.798 0 0 1 7.798-7.798H89.38a7.798 7.798 0 0 1 7.799 7.798v8.763h-2.4v-8.763a5.399 5.399 0 0 0-5.398-5.399H7.797A5.399 5.399 0 0 0 2.4 18.207v49.19a5.399 5.399 0 0 0 5.4 5.398h9.483v2.4H7.798A7.798 7.798 0 0 1 0 67.396V18.207Zm49.378 54.588h13.498v2.4H49.378v-2.4Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-text-headers" fill-rule="evenodd" d="M88.78 58.398c8.946 0 16.197-7.251 16.197-16.196s-7.251-16.197-16.196-16.197-16.197 7.252-16.197 16.197 7.252 16.196 16.197 16.196Zm0 2.4c10.27 0 18.597-8.326 18.597-18.596s-8.326-18.596-18.596-18.596-18.596 8.326-18.596 18.596S78.51 60.798 88.78 60.798Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-text-headers" fill-rule="evenodd" d="M61.005 87.192h54.28c1.303 0 1.833-.344 2.01-.53.128-.134.396-.517.226-1.586-2.187-13.74-14.213-24.278-28.752-24.278S62.203 71.337 60.017 85.076c-.09.57.026 1.226.279 1.662.115.2.23.305.316.359.072.046.184.095.393.095Zm0 2.4h54.28c3.346 0 5.104-1.76 4.605-4.893-2.371-14.903-15.402-26.3-31.121-26.3-15.72 0-28.75 11.397-31.122 26.3-.337 2.121.744 4.893 3.358 4.893Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M77.983 17.607a1.2 1.2 0 0 1-1.2-1.2v-.6a1.2 1.2 0 1 1 2.4 0v.6a1.2 1.2 0 0 1-1.2 1.2ZM83.382 17.607a1.2 1.2 0 0 1-1.2-1.2v-.498a1.2 1.2 0 1 1 2.4 0v.498a1.2 1.2 0 0 1-1.2 1.2ZM88.78 17.607a1.2 1.2 0 0 1-1.2-1.2v-.498a1.2 1.2 0 1 1 2.4 0v.498a1.2 1.2 0 0 1-1.2 1.2Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-text-headers" fill-rule="evenodd" d="M95.083 20.607H0v-1.2h95.083v1.2ZM15.12 54.571a5.999 5.999 0 0 1 6-5.998h23.23a5.999 5.999 0 0 1 6 5.998V57.2h-2.4V54.57a3.6 3.6 0 0 0-3.6-3.599H21.12a3.6 3.6 0 0 0-3.6 3.6v2.627h-2.4V54.57Zm2.4 15.825v2.693a3.6 3.6 0 0 0 3.6 3.599h23.23a3.6 3.6 0 0 0 3.6-3.6v-2.692h2.4v2.693a5.999 5.999 0 0 1-6 5.998H21.12a5.999 5.999 0 0 1-6-5.998v-2.693h2.4Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-text-headers" fill-rule="evenodd" d="M32.479 33.255c-5.31 0-9.641 4.332-9.641 9.64v6.822h-2.4v-6.821c0-6.635 5.406-12.04 12.04-12.04 6.633 0 12.041 5.377 12.041 12.04v6.821h-2.4v-6.821c0-5.334-4.33-9.641-9.64-9.641Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M10.498 63.797a7.498 7.498 0 0 1 7.498-7.498h30.593a7.498 7.498 0 0 1 0 14.997H17.996a7.498 7.498 0 0 1-7.498-7.499Zm43.79 0a5.699 5.699 0 0 0-5.699-5.699H17.996a5.699 5.699 0 0 0 0 11.398h30.593a5.699 5.699 0 0 0 5.7-5.699Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M19.02 60.669a.6.6 0 0 1 .6.6v2.959a.6.6 0 0 1-1.2 0v-2.96a.6.6 0 0 1 .6-.6Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M22.443 63.13a.6.6 0 0 1-.388.755l-2.851.914a.6.6 0 1 1-.367-1.142l2.852-.915a.6.6 0 0 1 .754.388Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M18.67 63.742a.6.6 0 0 1 .837.135l1.748 2.42a.6.6 0 0 1-.972.703l-1.749-2.42a.6.6 0 0 1 .136-.838Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M19.368 63.739a.6.6 0 0 1 .142.837l-1.722 2.42a.6.6 0 0 1-.978-.695l1.722-2.42a.6.6 0 0 1 .836-.142Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M15.626 63.129a.6.6 0 0 1 .755-.386l2.825.914a.6.6 0 0 1-.37 1.142l-2.824-.915a.6.6 0 0 1-.386-.755ZM28.651 60.669a.6.6 0 0 1 .6.6v2.959a.6.6 0 1 1-1.2 0v-2.96a.6.6 0 0 1 .6-.6Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M32.046 63.129a.6.6 0 0 1-.386.755l-2.824.915a.6.6 0 1 1-.37-1.142l2.825-.914a.6.6 0 0 1 .755.386Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M28.3 63.742a.6.6 0 0 1 .837.135l1.749 2.42a.6.6 0 0 1-.973.703l-1.748-2.42a.6.6 0 0 1 .135-.838Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M29.002 63.742a.6.6 0 0 1 .136.837L27.387 67a.6.6 0 0 1-.972-.702l1.749-2.421a.6.6 0 0 1 .837-.136Z" clip-rule="evenodd"/>
|
||||||
|
<path class="tw-fill-info-600" fill-rule="evenodd" d="M25.256 63.129a.6.6 0 0 1 .755-.386l2.825.914a.6.6 0 1 1-.37 1.142l-2.824-.915a.6.6 0 0 1-.386-.755ZM34.857 66.649a.6.6 0 0 1 .6-.6h5.649a.6.6 0 0 1 0 1.2h-5.649a.6.6 0 0 1-.6-.6ZM44.487 66.649a.6.6 0 0 1 .6-.6h5.65a.6.6 0 0 1 0 1.2h-5.65a.6.6 0 0 1-.6-.6Z" clip-rule="evenodd"/>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
@@ -16,7 +16,9 @@ export * from "./fingerprint-dialog/fingerprint-dialog.component";
|
|||||||
|
|
||||||
// password callout
|
// password callout
|
||||||
export * from "./password-callout/password-callout.component";
|
export * from "./password-callout/password-callout.component";
|
||||||
export * from "./vault-timeout-input/vault-timeout-input.component";
|
|
||||||
|
// password hint
|
||||||
|
export * from "./password-hint/password-hint.component";
|
||||||
|
|
||||||
// input password
|
// input password
|
||||||
export * from "./input-password/input-password.component";
|
export * from "./input-password/input-password.component";
|
||||||
@@ -40,3 +42,6 @@ export * from "./registration/registration-start/registration-start-secondary.co
|
|||||||
export * from "./registration/registration-env-selector/registration-env-selector.component";
|
export * from "./registration/registration-env-selector/registration-env-selector.component";
|
||||||
export * from "./registration/registration-finish/registration-finish.service";
|
export * from "./registration/registration-finish/registration-finish.service";
|
||||||
export * from "./registration/registration-finish/default-registration-finish.service";
|
export * from "./registration/registration-finish/default-registration-finish.service";
|
||||||
|
|
||||||
|
// vault timeout
|
||||||
|
export * from "./vault-timeout-input/vault-timeout-input.component";
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<form [bitSubmit]="submit" [formGroup]="formGroup">
|
||||||
|
<!-- <ng-template> must be within the <form> to ensure that `formControlName` has a parent `formGroup` directive. -->
|
||||||
|
<ng-template #formContentTemplate>
|
||||||
|
<bit-form-field>
|
||||||
|
<bit-label>{{ "accountEmail" | i18n }}</bit-label>
|
||||||
|
<input
|
||||||
|
bitInput
|
||||||
|
appAutofocus
|
||||||
|
inputmode="email"
|
||||||
|
appInputVerbatim="false"
|
||||||
|
type="email"
|
||||||
|
formControlName="email"
|
||||||
|
/>
|
||||||
|
</bit-form-field>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="tw-mb-2"
|
||||||
|
type="submit"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="primary"
|
||||||
|
[block]="true"
|
||||||
|
>
|
||||||
|
{{ "requestHint" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button type="button" bitButton buttonType="secondary" (click)="cancel()" [block]="true">
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<!-- Browser -->
|
||||||
|
<main *ngIf="clientType === 'browser'" tabindex="-1">
|
||||||
|
<ng-container *ngTemplateOutlet="formContentTemplate"></ng-container>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Web, Desktop -->
|
||||||
|
<ng-container *ngIf="clientType !== 'browser'">
|
||||||
|
<ng-container *ngTemplateOutlet="formContentTemplate"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</form>
|
||||||
107
libs/auth/src/angular/password-hint/password-hint.component.ts
Normal file
107
libs/auth/src/angular/password-hint/password-hint.component.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
|
import { Router, RouterModule } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { PasswordHintRequest } from "@bitwarden/common/auth/models/request/password-hint.request";
|
||||||
|
import { ClientType } from "@bitwarden/common/enums";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
FormFieldModule,
|
||||||
|
ToastService,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
templateUrl: "./password-hint.component.html",
|
||||||
|
imports: [
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CommonModule,
|
||||||
|
FormFieldModule,
|
||||||
|
JslibModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
RouterModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class PasswordHintComponent implements OnInit {
|
||||||
|
protected clientType: ClientType;
|
||||||
|
|
||||||
|
protected formGroup = this.formBuilder.group({
|
||||||
|
email: ["", [Validators.required, Validators.email]],
|
||||||
|
});
|
||||||
|
|
||||||
|
protected get email() {
|
||||||
|
return this.formGroup.controls.email.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private apiService: ApiService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private loginEmailService: LoginEmailServiceAbstraction,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private router: Router,
|
||||||
|
) {
|
||||||
|
this.clientType = this.platformUtilsService.getClientType();
|
||||||
|
}
|
||||||
|
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
const email = (await firstValueFrom(this.loginEmailService.loginEmail$)) ?? "";
|
||||||
|
this.formGroup.controls.email.setValue(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
submit = async () => {
|
||||||
|
const isEmailValid = this.validateEmailOrShowToast(this.email);
|
||||||
|
if (!isEmailValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.apiService.postPasswordHint(new PasswordHintRequest(this.email));
|
||||||
|
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("masterPassSent"),
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.router.navigate(["login"]);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected async cancel() {
|
||||||
|
this.loginEmailService.setLoginEmail(this.email);
|
||||||
|
await this.router.navigate(["login"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateEmailOrShowToast(email: string): boolean {
|
||||||
|
// If email is null or empty, show error toast and return false
|
||||||
|
if (email == null || email === "") {
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: this.i18nService.t("errorOccurred"),
|
||||||
|
message: this.i18nService.t("emailRequired"),
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not a valid email format, show error toast and return false
|
||||||
|
if (email.indexOf("@") === -1) {
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: this.i18nService.t("errorOccurred"),
|
||||||
|
message: this.i18nService.t("invalidEmail"),
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // email is valid
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user