1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-27 10:03:23 +00:00

[PM-32237] Add back functionality to email OTP auth flow (#19024)

* add back functionality to OTP auth flow

* respond to review comments

* hoist email value to parent component

---------

Co-authored-by: Alex Dragovich <46065570+itsadrago@users.noreply.github.com>
This commit is contained in:
John Harrington
2026-02-19 11:59:59 -07:00
committed by GitHub
parent 04aad44322
commit 8399815ea7
4 changed files with 60 additions and 15 deletions

View File

@@ -20,16 +20,18 @@
<bit-label>{{ "verificationCode" | i18n }}</bit-label> <bit-label>{{ "verificationCode" | i18n }}</bit-label>
<input bitInput type="text" [formControl]="otp" required appInputVerbatim appAutofocus /> <input bitInput type="text" [formControl]="otp" required appInputVerbatim appAutofocus />
</bit-form-field> </bit-form-field>
<div class="tw-flex"> <button
<button bitButton
bitButton bitFormButton
bitFormButton type="submit"
type="submit" buttonType="primary"
buttonType="primary" [loading]="loading()"
[loading]="loading()" [block]="true"
[block]="true" class="tw-mb-3"
> >
<span>{{ "viewSend" | i18n }} </span> <span>{{ "viewSend" | i18n }} </span>
</button> </button>
</div> <button bitButton type="button" buttonType="secondary" [block]="true" (click)="onBackClick()">
<span>{{ "back" | i18n }}</span>
</button>
} }

View File

@@ -1,6 +1,14 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { ChangeDetectionStrategy, Component, input, OnDestroy, OnInit } from "@angular/core"; import {
ChangeDetectionStrategy,
Component,
effect,
input,
OnDestroy,
OnInit,
output,
} from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms"; import { FormControl, FormGroup, Validators } from "@angular/forms";
import { SharedModule } from "../../../shared"; import { SharedModule } from "../../../shared";
@@ -18,18 +26,45 @@ export class SendAccessEmailComponent implements OnInit, OnDestroy {
protected otp: FormControl; protected otp: FormControl;
readonly loading = input.required<boolean>(); readonly loading = input.required<boolean>();
readonly backToEmail = output<void>();
constructor() {} constructor() {}
ngOnInit() { ngOnInit() {
this.email = new FormControl("", Validators.required); this.email = new FormControl("", Validators.required);
this.otp = new FormControl("", Validators.required); this.otp = new FormControl("");
this.formGroup().addControl("email", this.email); this.formGroup().addControl("email", this.email);
this.formGroup().addControl("otp", this.otp); this.formGroup().addControl("otp", this.otp);
}
// Update validators when enterOtp changes
effect(() => {
const isOtpMode = this.enterOtp();
if (isOtpMode) {
// In OTP mode: email is not required (already entered), otp is required
this.email.clearValidators();
this.otp.setValidators([Validators.required]);
} else {
// In email mode: email is required, otp is not required
this.email.setValidators([Validators.required]);
this.otp.clearValidators();
}
this.email.updateValueAndValidity();
this.otp.updateValueAndValidity();
});
}
ngOnDestroy() { ngOnDestroy() {
this.formGroup().removeControl("email"); this.formGroup().removeControl("email");
this.formGroup().removeControl("otp"); this.formGroup().removeControl("otp");
} }
onBackClick() {
this.backToEmail.emit();
if (this.otp) {
this.otp.clearValidators();
this.otp.setValue("");
this.otp.setErrors(null);
this.otp.markAsUntouched();
this.otp.markAsPristine();
}
}
} }

View File

@@ -31,6 +31,7 @@
[formGroup]="sendAccessForm" [formGroup]="sendAccessForm"
[enterOtp]="enterOtp()" [enterOtp]="enterOtp()"
[loading]="loading()" [loading]="loading()"
(backToEmail)="onBackToEmail()"
></app-send-access-email> ></app-send-access-email>
} }
} }

View File

@@ -90,6 +90,11 @@ export class SendAuthComponent implements OnInit {
this.loading.set(false); this.loading.set(false);
} }
onBackToEmail() {
this.enterOtp.set(false);
this.updatePageTitle();
}
private async attemptV1Access() { private async attemptV1Access() {
try { try {
const accessRequest = new SendAccessRequest(); const accessRequest = new SendAccessRequest();
@@ -247,10 +252,12 @@ export class SendAuthComponent implements OnInit {
if (this.enterOtp()) { if (this.enterOtp()) {
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
pageTitle: { key: "enterTheCodeSentToYourEmail" }, pageTitle: { key: "enterTheCodeSentToYourEmail" },
pageSubtitle: this.sendAccessForm.value.email ?? null,
}); });
} else { } else {
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
pageTitle: { key: "verifyYourEmailToViewThisSend" }, pageTitle: { key: "verifyYourEmailToViewThisSend" },
pageSubtitle: null,
}); });
} }
} else if (authType === AuthType.Password) { } else if (authType === AuthType.Password) {