1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 00:03:56 +00:00

Auth/PM-7324 - Registration with Email Verification - Registration Start Component Implementation (#9573)

* PM-7324 - Register new registration start comp at signup route on web

* PM-7324 - Add registerSendVerificationEmail logic in API service layer.

* PM-7324 - Update registration start comp to actually send information to API and trigger email.

* PM-7324 - progress on opt in for marketing emails redesign.

* PM-7324 - Add feature flag and feature flag guard to sign up route.

* PM-7324 - RegistrationEnvSelector - emit initial value

* PM-7324 - Registration Start comp - wire up setReceiveMarketingEmailsByRegion logic.

* PM-7324 - Registration start html - use proper link for email pref management.

* PM-7324 - Translate text

* PM-7324 - Design pass

* PM-7324 - design pass v2

* PM-7324 - Update Tailwind config to add availability of anon layout to desktop and browser extension

* PM-7324 - Desktop - AppRoutingModule - Add new signup route protected by the email verification feature flag.

* PM-7324 - BrowserExtension - AppRoutingModule - Add signup route protected by feature flag

* PM-7324 - Feature flag all register page navigations to redirect users to the new signup page.

* PM-7324 - Update AnonLayoutWrapperComponent constructor logic to avoid passing undefined values into I18nService.t method

* PM-7324 - Accept org invite web comp - adjust register url and qParams

* PM-7324 - Add AnonLayoutWrapperData to desktop & browser since we don't need titleId.

* PM-7324 - Revert anon layout wrapper comp changes as they were made separately and merged to main.

* PM-7234 - Fix registration start component so the login route works for the browser extension.

* PM-7324 - Registration start story now building again + fix storybook warning around BrowserAnimationsModule

* PM-7324 - Registration Start - add missing tw-text-main to fix dark mode rendering.

* PM-7324 - Update storybook docs

* PM-7324 - Get stub of registration finish component so that the verify email has something to land on.

* PM-7324 - Registration start - receive marketing materials should never be required.

* PM-7324 - Add finish signup route + required translations to desktop & browser.

* PM-7324 - AnonLayoutWrapperComponent - Resolve issues where navigating to a sibling anonymous route wouldn't update the AnonLayoutWrapperData.

* PM-7324 - Remove unnecessary array

* PM-7324  - Per PR feedback, improve setReceiveMarketingEmailsByRegion

* PM-7324 - Per PR feedback, inject login routes via route data

* PM-7324 - Document methods in account api service

* PM-7324 - PR feedback - jsdoc tweaks
This commit is contained in:
Jared Snider
2024-06-14 11:40:56 -04:00
committed by GitHub
parent eb96f7dbfb
commit 215bbc2f8e
42 changed files with 584 additions and 88 deletions

View File

@@ -27,7 +27,7 @@ export class AcceptFamilySponsorshipComponent extends BaseAcceptComponent {
} else {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["/register"], { queryParams: { email: qParams.email } });
this.router.navigate([this.registerRoute], { queryParams: { email: qParams.email } });
}
}
}

View File

@@ -29,7 +29,7 @@
<a
bitButton
buttonType="primary"
routerLink="/register"
[routerLink]="registerRoute"
[queryParams]="{ email: email }"
[block]="true"
>

View File

@@ -2,6 +2,7 @@ import { Component } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -28,9 +29,10 @@ export class AcceptEmergencyComponent extends BaseAcceptComponent {
i18nService: I18nService,
route: ActivatedRoute,
authService: AuthService,
configService: ConfigService,
private emergencyAccessService: EmergencyAccessService,
) {
super(router, platformUtilsService, i18nService, route, authService);
super(router, platformUtilsService, i18nService, route, authService, configService);
}
async authedHandler(qParams: Params): Promise<void> {

View File

@@ -56,7 +56,7 @@
<p class="tw-m-0 tw-text-sm">
{{ "newAroundHere" | i18n }}
<!--mousedown event is used over click because it prevents the validation from firing -->
<a routerLink="/register" (mousedown)="goToRegister()">{{ "createAccount" | i18n }}</a>
<a bitLink href="#" (mousedown)="goToRegister()">{{ "createAccount" | i18n }}</a>
</p>
</ng-container>

View File

@@ -20,6 +20,7 @@ import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -67,6 +68,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
loginEmailService: LoginEmailServiceAbstraction,
ssoLoginService: SsoLoginServiceAbstraction,
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
configService: ConfigService,
) {
super(
devicesApiService,
@@ -87,6 +89,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
loginEmailService,
ssoLoginService,
webAuthnLoginService,
configService,
);
this.onSuccessfulLoginNavigate = this.goAfterLogIn;
this.showPasswordless = flagEnabled("showPasswordless");
@@ -165,11 +168,11 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
const email = this.formGroup.value.email;
if (email) {
await this.router.navigate(["/register"], { queryParams: { email: email } });
await this.router.navigate([this.registerRoute], { queryParams: { email: email } });
return;
}
await this.router.navigate(["/register"]);
await this.router.navigate([this.registerRoute]);
}
protected override async handleMigrateEncryptionKey(result: AuthResult): Promise<boolean> {

View File

@@ -32,7 +32,7 @@
{{ "logIn" | i18n }}
</a>
<a
routerLink="/register"
[routerLink]="registerRoute"
[queryParams]="{ email: email }"
class="btn btn-primary btn-block ml-2 mt-0"
>

View File

@@ -2,6 +2,7 @@ import { Component } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -23,9 +24,10 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
i18nService: I18nService,
route: ActivatedRoute,
authService: AuthService,
configService: ConfigService,
private acceptOrganizationInviteService: AcceptOrganizationInviteService,
) {
super(router, platformUtilsService, i18nService, route, authService);
super(router, platformUtilsService, i18nService, route, authService, configService);
}
async authedHandler(qParams: Params): Promise<void> {
@@ -86,8 +88,26 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
// if SSO is disabled OR if sso is enabled but the SSO login required policy is not enabled
// then send user to create account
await this.router.navigate(["/register"], {
queryParams: { email: invite.email, fromOrgInvite: true },
// TODO: update logic when email verification flag is removed
let queryParams: Params;
if (this.registerRoute === "/register") {
queryParams = {
fromOrgInvite: "true",
email: invite.email,
};
} else if (this.registerRoute === "/signup") {
// We have to override the base component route b/c it is correct for other components
// that extend the base accept comp. We don't need users to complete email verification
// if they are coming directly from an emailed org invite.
this.registerRoute = "/finish-signup";
queryParams = {
email: invite.email,
};
}
await this.router.navigate([this.registerRoute], {
queryParams: queryParams,
});
return;
}

View File

@@ -5,6 +5,8 @@ import { first, switchMap, takeUntil } from "rxjs/operators";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -19,6 +21,9 @@ export abstract class BaseAcceptComponent implements OnInit {
protected failedShortMessage = "inviteAcceptFailedShort";
protected failedMessage = "inviteAcceptFailed";
// TODO: remove when email verification flag is removed
registerRoute = "/register";
private destroy$ = new Subject<void>();
constructor(
@@ -27,12 +32,22 @@ export abstract class BaseAcceptComponent implements OnInit {
protected i18nService: I18nService,
protected route: ActivatedRoute,
protected authService: AuthService,
private configService: ConfigService,
) {}
abstract authedHandler(qParams: Params): Promise<void>;
abstract unauthedHandler(qParams: Params): Promise<void>;
ngOnInit() {
async ngOnInit() {
// TODO: remove when email verification flag is removed
const emailVerification = await this.configService.getFeatureFlag(
FeatureFlag.EmailVerification,
);
if (emailVerification) {
this.registerRoute = "/signup";
}
this.route.queryParams
.pipe(
first(),

View File

@@ -9,7 +9,16 @@ import {
UnauthGuard,
unauthGuardFn,
} from "@bitwarden/angular/auth/guards";
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/auth/angular";
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
import {
AnonLayoutWrapperComponent,
AnonLayoutWrapperData,
RegistrationFinishComponent,
RegistrationStartComponent,
RegistrationStartSecondaryComponent,
RegistrationStartSecondaryComponentData,
} from "@bitwarden/auth/angular";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { flagEnabled, Flags } from "../utils/flags";
@@ -175,6 +184,41 @@ const routes: Routes = [
path: "",
component: AnonLayoutWrapperComponent,
children: [
{
path: "signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: { pageTitle: "createAccount", titleId: "createAccount" } satisfies DataProperties &
AnonLayoutWrapperData,
children: [
{
path: "",
component: RegistrationStartComponent,
},
{
path: "",
component: RegistrationStartSecondaryComponent,
outlet: "secondary",
data: {
loginRoute: "/login",
} satisfies RegistrationStartSecondaryComponentData,
},
],
},
{
path: "finish-signup",
canActivate: [canAccessFeature(FeatureFlag.EmailVerification), unauthGuardFn()],
data: {
pageTitle: "setAStrongPassword",
pageSubtitle: "finishCreatingYourAccountBySettingAPassword",
titleId: "setAStrongPassword",
} satisfies DataProperties & AnonLayoutWrapperData,
children: [
{
path: "",
component: RegistrationFinishComponent,
},
],
},
{
path: "sso",
canActivate: [unauthGuardFn()],

View File

@@ -75,7 +75,7 @@
>Bitwarden Send</a
>
{{ "sendAccessTaglineOr" | i18n }}
<a bitLink routerLink="/register" target="_blank" rel="noreferrer">{{
<a bitLink [routerLink]="registerRoute" target="_blank" rel="noreferrer">{{
"sendAccessTaglineSignUp" | i18n
}}</a>
{{ "sendAccessTaglineTryToday" | i18n }}

View File

@@ -2,7 +2,9 @@ import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -54,6 +56,9 @@ export class AccessComponent implements OnInit {
protected formGroup = this.formBuilder.group({});
// TODO: remove when email verification flag is removed
registerRoute = "/register";
private id: string;
private key: string;
@@ -64,6 +69,7 @@ export class AccessComponent implements OnInit {
private sendApiService: SendApiService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private configService: ConfigService,
protected formBuilder: FormBuilder,
) {}
@@ -81,7 +87,16 @@ export class AccessComponent implements OnInit {
return this.send.creatorIdentifier;
}
ngOnInit() {
async ngOnInit() {
// TODO: remove when email verification flag is removed
const emailVerification = await this.configService.getFeatureFlag(
FeatureFlag.EmailVerification,
);
if (emailVerification) {
this.registerRoute = "/signup";
}
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.params.subscribe(async (params) => {
this.id = params.sendId;

View File

@@ -713,6 +713,12 @@
"createAccount": {
"message": "Create account"
},
"setAStrongPassword": {
"message": "Set a strong password"
},
"finishCreatingYourAccountBySettingAPassword": {
"message": "Finish creating your account by setting a password"
},
"newAroundHere": {
"message": "New around here?"
},
@@ -3724,6 +3730,21 @@
"nothingSelected": {
"message": "You have not selected anything."
},
"receiveMarketingEmails": {
"message": "Get emails from Bitwarden for announcements, advice, and research opportunities."
},
"unsubscribe": {
"message": "Unsubscribe"
},
"atAnyTime": {
"message": "at any time."
},
"byContinuingYouAgreeToThe": {
"message": "By continuing, you agree to the"
},
"and": {
"message": "and"
},
"acceptPolicies": {
"message": "By checking this box you agree to the following:"
},