Skip to content

Commit 41a240e

Browse files
committed
fix(angular): Ensure recaptcha container is deferred until view child is ready
1 parent e52ee10 commit 41a240e

File tree

8 files changed

+72
-23
lines changed

8 files changed

+72
-23
lines changed

packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-assertion-form.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe("<fui-sms-multi-factor-assertion-form>", () => {
3434
injectUI,
3535
injectMultiFactorPhoneAuthNumberFormSchema,
3636
injectMultiFactorPhoneAuthVerifyFormSchema,
37+
injectRecaptchaVerifier,
3738
} = require("../../../tests/test-helpers");
3839

3940
injectTranslation.mockImplementation((category: string, key: string) => {
@@ -74,6 +75,14 @@ describe("<fui-sms-multi-factor-assertion-form>", () => {
7475
verifyPhoneNumber.mockResolvedValue("test-verification-id");
7576
signInWithMultiFactorAssertion.mockResolvedValue({});
7677

78+
injectRecaptchaVerifier.mockImplementation(() => {
79+
return () => ({
80+
clear: jest.fn(),
81+
render: jest.fn(),
82+
verify: jest.fn(),
83+
});
84+
});
85+
7786
const { PhoneAuthProvider, PhoneMultiFactorGenerator } = require("firebase/auth");
7887
PhoneAuthProvider.credential = jest.fn().mockReturnValue({});
7988
PhoneMultiFactorGenerator.assertion = jest.fn().mockReturnValue({});

packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-assertion-form.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export class SmsMultiFactorAssertionPhoneFormComponent {
8484
return hint.phoneNumber || "";
8585
});
8686

87-
recaptchaVerifier = injectRecaptchaVerifier(this.recaptchaContainer());
87+
recaptchaVerifier = injectRecaptchaVerifier(() => this.recaptchaContainer());
8888

8989
form = injectForm({
9090
defaultValues: {
@@ -107,13 +107,12 @@ export class SmsMultiFactorAssertionPhoneFormComponent {
107107
onSubmit: this.formSchema(),
108108
onSubmitAsync: async () => {
109109
try {
110-
const verificationId = await verifyPhoneNumber(
111-
this.ui(),
112-
"",
113-
this.recaptchaVerifier(),
114-
undefined,
115-
this.hint()
116-
);
110+
const verifier = this.recaptchaVerifier();
111+
if (!verifier) {
112+
return this.unknownErrorLabel();
113+
}
114+
115+
const verificationId = await verifyPhoneNumber(this.ui(), "", verifier, undefined, this.hint());
117116
this.onSubmit.emit(verificationId);
118117
return;
119118
} catch (error) {
@@ -130,7 +129,9 @@ export class SmsMultiFactorAssertionPhoneFormComponent {
130129
effect((onCleanup) => {
131130
const verifier = this.recaptchaVerifier();
132131
onCleanup(() => {
133-
verifier.clear();
132+
if (verifier) {
133+
verifier.clear();
134+
}
134135
});
135136
});
136137
}

packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-enrollment-form.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describe("<fui-sms-multi-factor-enrollment-form />", () => {
4242
injectMultiFactorPhoneAuthNumberFormSchema,
4343
injectMultiFactorPhoneAuthVerifyFormSchema,
4444
injectDefaultCountry,
45+
injectRecaptchaVerifier,
4546
} = require("../../../tests/test-helpers");
4647
const { PhoneAuthProvider, PhoneMultiFactorGenerator, multiFactor } = require("../../../tests/test-helpers");
4748

@@ -88,6 +89,14 @@ describe("<fui-sms-multi-factor-enrollment-form />", () => {
8889
injectDefaultCountry.mockImplementation(() => {
8990
return () => ({ code: "US" });
9091
});
92+
93+
injectRecaptchaVerifier.mockImplementation(() => {
94+
return () => ({
95+
clear: jest.fn(),
96+
render: jest.fn(),
97+
verify: jest.fn(),
98+
});
99+
});
91100
});
92101

93102
afterEach(() => {

packages/angular/src/lib/auth/forms/mfa/sms-multi-factor-enrollment-form.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export class SmsMultiFactorEnrollmentFormComponent {
127127

128128
recaptchaContainer = viewChild.required<ElementRef<HTMLDivElement>>("recaptchaContainer");
129129

130-
recaptchaVerifier = injectRecaptchaVerifier(this.recaptchaContainer());
130+
recaptchaVerifier = injectRecaptchaVerifier(() => this.recaptchaContainer());
131131

132132
phoneForm = injectForm({
133133
defaultValues: {
@@ -158,14 +158,14 @@ export class SmsMultiFactorEnrollmentFormComponent {
158158
throw new Error("User must be authenticated to enroll with multi-factor authentication");
159159
}
160160

161+
const verifier = this.recaptchaVerifier();
162+
if (!verifier) {
163+
return this.unknownErrorLabel();
164+
}
165+
161166
const mfaUser = multiFactor(currentUser);
162167
const formattedPhoneNumber = formatPhoneNumber(value.phoneNumber, this.defaultCountry());
163-
const verificationId = await verifyPhoneNumber(
164-
this.ui(),
165-
formattedPhoneNumber,
166-
this.recaptchaVerifier(),
167-
mfaUser
168-
);
168+
const verificationId = await verifyPhoneNumber(this.ui(), formattedPhoneNumber, verifier, mfaUser);
169169

170170
this.displayName.set(value.displayName);
171171
this.verificationId.set(verificationId);

packages/angular/src/lib/auth/forms/phone-auth-form.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,19 @@ describe("<fui-phone-auth-form />", () => {
3434

3535
beforeEach(() => {
3636
const { verifyPhoneNumber, confirmPhoneNumber, formatPhoneNumber, FirebaseUIError } = require("@firebase-ui/core");
37+
const { injectRecaptchaVerifier } = require("../../tests/test-helpers");
3738
mockVerifyPhoneNumber = verifyPhoneNumber;
3839
mockConfirmPhoneNumber = confirmPhoneNumber;
3940
mockFormatPhoneNumber = formatPhoneNumber;
4041
mockFirebaseUIError = FirebaseUIError;
42+
43+
injectRecaptchaVerifier.mockImplementation(() => {
44+
return () => ({
45+
clear: jest.fn(),
46+
render: jest.fn(),
47+
verify: jest.fn(),
48+
});
49+
});
4150
});
4251

4352
afterEach(() => {

packages/angular/src/lib/auth/forms/phone-auth-form.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export class PhoneNumberFormComponent {
8686
unknownErrorLabel = injectTranslation("errors", "unknownError");
8787

8888
recaptchaContainer = viewChild.required<ElementRef<HTMLDivElement>>("recaptchaContainer");
89-
recaptchaVerifier = injectRecaptchaVerifier(this.recaptchaContainer());
89+
recaptchaVerifier = injectRecaptchaVerifier(() => this.recaptchaContainer());
9090

9191
form = injectForm({
9292
defaultValues: {
@@ -107,7 +107,11 @@ export class PhoneNumberFormComponent {
107107
const formattedNumber = formatPhoneNumber(value.phoneNumber, selectedCountry!);
108108

109109
try {
110-
const verificationId = await verifyPhoneNumber(this.ui(), formattedNumber, this.recaptchaVerifier());
110+
const verifier = this.recaptchaVerifier();
111+
if (!verifier) {
112+
return this.unknownErrorLabel();
113+
}
114+
const verificationId = await verifyPhoneNumber(this.ui(), formattedNumber, verifier);
111115
this.onSubmit.emit({ verificationId, phoneNumber: formattedNumber });
112116
return;
113117
} catch (error) {
@@ -126,7 +130,9 @@ export class PhoneNumberFormComponent {
126130
const verifier = this.recaptchaVerifier();
127131

128132
onCleanup(() => {
129-
verifier.clear();
133+
if (verifier) {
134+
verifier.clear();
135+
}
130136
});
131137
});
132138
}

packages/angular/src/lib/provider.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,20 @@ export function injectUI() {
8989
return ui.asReadonly();
9090
}
9191

92-
export function injectRecaptchaVerifier(element: ElementRef<HTMLDivElement>) {
92+
export function injectRecaptchaVerifier(element: () => ElementRef<HTMLDivElement>) {
9393
const ui = injectUI();
94-
const verifier = computed(() => getBehavior(ui(), "recaptchaVerification")(ui(), element.nativeElement));
94+
const verifier = computed(() => {
95+
const elementRef = element();
96+
if (!elementRef) {
97+
return null;
98+
}
99+
return getBehavior(ui(), "recaptchaVerification")(ui(), elementRef.nativeElement);
100+
});
95101

96102
effect(() => {
97-
if (verifier()) {
98-
verifier().render();
103+
const verifierInstance = verifier();
104+
if (verifierInstance) {
105+
verifierInstance.render();
99106
}
100107
});
101108

packages/angular/src/lib/tests/test-helpers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,14 @@ export const injectMultiFactorTotpAuthEnrollmentFormSchema = jest.fn().mockRetur
273273
export const injectCountries = jest.fn().mockReturnValue(() => countryData);
274274
export const injectDefaultCountry = jest.fn().mockReturnValue(() => "US");
275275

276+
export const injectRecaptchaVerifier = jest.fn().mockImplementation(() => {
277+
return () => ({
278+
clear: jest.fn(),
279+
render: jest.fn(),
280+
verify: jest.fn(),
281+
});
282+
});
283+
276284
export const RecaptchaVerifier = jest.fn().mockImplementation(() => ({
277285
clear: jest.fn(),
278286
render: jest.fn(),

0 commit comments

Comments
 (0)