1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-23 03:22:50 +02:00

Auth/PM-11969 - Registration with Email Verification - Accept Emergency Access Invite Flow (#11018)

* PM-11969 - Registration with Email Verification - Accept Emergency Access Invite Fixed

* PM-11969 - Fix PR feedback

* PM-11969 - AcceptEmergencyComponent - remove prop
This commit is contained in:
Jared Snider 2024-09-12 20:14:37 -04:00 committed by GitHub
parent fe96aa85f2
commit bd3863c313
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 66 additions and 8 deletions

View File

@ -71,6 +71,8 @@ export class WebRegistrationFinishService
userAsymmetricKeys: [string, EncString], userAsymmetricKeys: [string, EncString],
emailVerificationToken?: string, emailVerificationToken?: string,
orgSponsoredFreeFamilyPlanToken?: string, orgSponsoredFreeFamilyPlanToken?: string,
acceptEmergencyAccessInviteToken?: string,
emergencyAccessId?: string,
): Promise<RegisterFinishRequest> { ): Promise<RegisterFinishRequest> {
const registerRequest = await super.buildRegisterRequest( const registerRequest = await super.buildRegisterRequest(
email, email,
@ -94,6 +96,11 @@ export class WebRegistrationFinishService
registerRequest.orgSponsoredFreeFamilyPlanToken = orgSponsoredFreeFamilyPlanToken; registerRequest.orgSponsoredFreeFamilyPlanToken = orgSponsoredFreeFamilyPlanToken;
} }
if (acceptEmergencyAccessInviteToken && emergencyAccessId) {
registerRequest.acceptEmergencyAccessInviteToken = acceptEmergencyAccessInviteToken;
registerRequest.acceptEmergencyAccessId = emergencyAccessId;
}
return registerRequest; return registerRequest;
} }
} }

View File

@ -26,14 +26,8 @@
> >
{{ "logIn" | i18n }} {{ "logIn" | i18n }}
</a> </a>
<a <button type="button" bitButton buttonType="primary" (click)="register()" [block]="true">
bitButton
buttonType="primary"
[routerLink]="registerRoute$ | async"
[queryParams]="{ email: email }"
[block]="true"
>
{{ "createAccount" | i18n }} {{ "createAccount" | i18n }}
</a> </button>
</div> </div>
</div> </div>

View File

@ -1,5 +1,6 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router"; import { ActivatedRoute, Params, Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { RegisterRouteService } from "@bitwarden/auth/common"; import { RegisterRouteService } from "@bitwarden/auth/common";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
@ -18,6 +19,8 @@ import { EmergencyAccessService } from "../services/emergency-access.service";
}) })
export class AcceptEmergencyComponent extends BaseAcceptComponent { export class AcceptEmergencyComponent extends BaseAcceptComponent {
name: string; name: string;
emergencyAccessId: string;
acceptEmergencyAccessInviteToken: string;
protected requiredParameters: string[] = ["id", "name", "email", "token"]; protected requiredParameters: string[] = ["id", "name", "email", "token"];
protected failedShortMessage = "emergencyInviteAcceptFailedShort"; protected failedShortMessage = "emergencyInviteAcceptFailedShort";
@ -55,5 +58,36 @@ export class AcceptEmergencyComponent extends BaseAcceptComponent {
// Fix URL encoding of space issue with Angular // Fix URL encoding of space issue with Angular
this.name = this.name.replace(/\+/g, " "); this.name = this.name.replace(/\+/g, " ");
} }
if (qParams.id) {
this.emergencyAccessId = qParams.id;
}
if (qParams.token) {
this.acceptEmergencyAccessInviteToken = qParams.token;
}
}
async register() {
let queryParams: Params;
let registerRoute = await firstValueFrom(this.registerRoute$);
if (registerRoute === "/register") {
queryParams = {
email: this.email,
};
} else if (registerRoute === "/signup") {
// We have to override the base component route as we don't need users to
// complete email verification if they are coming directly an emailed invite.
registerRoute = "/finish-signup";
queryParams = {
email: this.email,
acceptEmergencyAccessInviteToken: this.acceptEmergencyAccessInviteToken,
emergencyAccessId: this.emergencyAccessId,
};
}
await this.router.navigate([registerRoute], {
queryParams: queryParams,
});
} }
} }

View File

@ -24,6 +24,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
passwordInputResult: PasswordInputResult, passwordInputResult: PasswordInputResult,
emailVerificationToken?: string, emailVerificationToken?: string,
orgSponsoredFreeFamilyPlanToken?: string, orgSponsoredFreeFamilyPlanToken?: string,
acceptEmergencyAccessInviteToken?: string,
emergencyAccessId?: string,
): Promise<string> { ): Promise<string> {
const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey( const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey(
passwordInputResult.masterKey, passwordInputResult.masterKey,
@ -41,6 +43,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
userAsymmetricKeys, userAsymmetricKeys,
emailVerificationToken, emailVerificationToken,
orgSponsoredFreeFamilyPlanToken, orgSponsoredFreeFamilyPlanToken,
acceptEmergencyAccessInviteToken,
emergencyAccessId,
); );
const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest); const capchaBypassToken = await this.accountApiService.registerFinish(registerRequest);
@ -55,6 +59,8 @@ export class DefaultRegistrationFinishService implements RegistrationFinishServi
userAsymmetricKeys: [string, EncString], userAsymmetricKeys: [string, EncString],
emailVerificationToken?: string, emailVerificationToken?: string,
orgSponsoredFreeFamilyPlanToken?: string, // web only orgSponsoredFreeFamilyPlanToken?: string, // web only
acceptEmergencyAccessInviteToken?: string, // web only
emergencyAccessId?: string, // web only
): Promise<RegisterFinishRequest> { ): Promise<RegisterFinishRequest> {
const userAsymmetricKeysRequest = new KeysRequest( const userAsymmetricKeysRequest = new KeysRequest(
userAsymmetricKeys[0], userAsymmetricKeys[0],

View File

@ -44,6 +44,10 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy {
// setup a free family plan sponsored by an organization but they don't have an account yet. // setup a free family plan sponsored by an organization but they don't have an account yet.
orgSponsoredFreeFamilyPlanToken: string; orgSponsoredFreeFamilyPlanToken: string;
// this token is provided when the user is coming from an emailed invite to accept an emergency access invite
acceptEmergencyAccessInviteToken: string;
emergencyAccessId: string;
masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null; masterPasswordPolicyOptions: MasterPasswordPolicyOptions | null = null;
constructor( constructor(
@ -79,6 +83,11 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy {
if (qParams.orgSponsoredFreeFamilyPlanToken != null) { if (qParams.orgSponsoredFreeFamilyPlanToken != null) {
this.orgSponsoredFreeFamilyPlanToken = qParams.orgSponsoredFreeFamilyPlanToken; this.orgSponsoredFreeFamilyPlanToken = qParams.orgSponsoredFreeFamilyPlanToken;
} }
if (qParams.acceptEmergencyAccessInviteToken != null && qParams.emergencyAccessId) {
this.acceptEmergencyAccessInviteToken = qParams.acceptEmergencyAccessInviteToken;
this.emergencyAccessId = qParams.emergencyAccessId;
}
}), }),
switchMap((qParams: Params) => { switchMap((qParams: Params) => {
if ( if (
@ -111,6 +120,8 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy {
passwordInputResult, passwordInputResult,
this.emailVerificationToken, this.emailVerificationToken,
this.orgSponsoredFreeFamilyPlanToken, this.orgSponsoredFreeFamilyPlanToken,
this.acceptEmergencyAccessInviteToken,
this.emergencyAccessId,
); );
} catch (e) { } catch (e) {
this.validationService.showError(e); this.validationService.showError(e);

View File

@ -16,6 +16,8 @@ export abstract class RegistrationFinishService {
* @param passwordInputResult The password input result. * @param passwordInputResult The password input result.
* @param emailVerificationToken The optional email verification token. Not present in emailed invite scenarios (ex: org invite). * @param emailVerificationToken The optional email verification token. Not present in emailed invite scenarios (ex: org invite).
* @param orgSponsoredFreeFamilyPlanToken The optional org sponsored free family plan token. * @param orgSponsoredFreeFamilyPlanToken The optional org sponsored free family plan token.
* @param acceptEmergencyAccessInviteToken The optional accept emergency access invite token.
* @param emergencyAccessId The optional emergency access id which is required to validate the emergency access invite token.
* Returns a promise which resolves to the captcha bypass token string upon a successful account creation. * Returns a promise which resolves to the captcha bypass token string upon a successful account creation.
*/ */
abstract finishRegistration( abstract finishRegistration(
@ -23,5 +25,7 @@ export abstract class RegistrationFinishService {
passwordInputResult: PasswordInputResult, passwordInputResult: PasswordInputResult,
emailVerificationToken?: string, emailVerificationToken?: string,
orgSponsoredFreeFamilyPlanToken?: string, orgSponsoredFreeFamilyPlanToken?: string,
acceptEmergencyAccessInviteToken?: string,
emergencyAccessId?: string,
): Promise<string>; ): Promise<string>;
} }

View File

@ -19,6 +19,8 @@ export class RegisterFinishRequest {
public emailVerificationToken?: string, public emailVerificationToken?: string,
public orgSponsoredFreeFamilyPlanToken?: string, public orgSponsoredFreeFamilyPlanToken?: string,
public acceptEmergencyAccessInviteToken?: string,
public acceptEmergencyAccessId?: string,
// Org Invite data (only applies on web) // Org Invite data (only applies on web)
public organizationUserId?: string, public organizationUserId?: string,