Skip to content

Commit 604e015

Browse files
committed
Merge branch '@invertase/v7-development' of https://github.com/firebase/firebaseui-web into @invertase/shadcn-registry
2 parents ba69050 + 11119ae commit 604e015

File tree

61 files changed

+4295
-279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+4295
-279
lines changed

examples/react/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,22 @@
1818
"@firebase-ui/styles": "workspace:*",
1919
"@firebase-ui/translations": "workspace:*",
2020
"firebase": "^11.6.0",
21-
"react": "^19.0.0",
22-
"react-dom": "^19.0.0",
21+
"react": "catalog:",
22+
"react-dom": "catalog:",
2323
"react-router": "^7.5.1"
2424
},
2525
"devDependencies": {
2626
"@tailwindcss/vite": "^4.1.4",
2727
"@eslint/js": "^9.22.0",
28-
"@types/react": "^19.0.10",
29-
"@types/react-dom": "^19.0.4",
30-
"@vitejs/plugin-react": "^4.3.4",
28+
"@types/react": "catalog:",
29+
"@types/react-dom": "catalog:",
30+
"@vitejs/plugin-react": "catalog:",
3131
"eslint": "^9.22.0",
3232
"eslint-plugin-react-hooks": "^5.2.0",
3333
"eslint-plugin-react-refresh": "^0.4.19",
3434
"globals": "^16.0.0",
3535
"prettier": "^3.1.1",
36-
"vite": "^6.3.1",
37-
"tailwindcss": "^4.1.4"
36+
"vite": "catalog:",
37+
"tailwindcss": "catalog:"
3838
}
3939
}

examples/react/src/App.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ function App() {
8282
Password Reset Screen
8383
</NavLink>
8484
</li>
85+
<li>
86+
<NavLink to="/screens/mfa-enrollment-screen" className="text-blue-500 hover:underline">
87+
MFA Enrollment Screen
88+
</NavLink>
89+
</li>
8590
</ul>
8691
</div>
8792
</div>

examples/react/src/main.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ import OAuthScreenPage from "./screens/oauth-screen";
4747
/** Password Reset */
4848
import ForgotPasswordPage from "./screens/forgot-password-screen";
4949

50+
/** MFA Enrollment */
51+
import MultiFactorAuthEnrollmentScreenPage from "./screens/mfa-enrollment-screen";
52+
5053
const root = document.getElementById("root")!;
5154

5255
ReactDOM.createRoot(root).render(
@@ -72,6 +75,7 @@ ReactDOM.createRoot(root).render(
7275
<Route path="/screens/sign-up-auth-screen-w-oauth" element={<SignUpAuthScreenWithOAuthPage />} />
7376
<Route path="/screens/oauth-screen" element={<OAuthScreenPage />} />
7477
<Route path="/screens/forgot-password-screen" element={<ForgotPasswordPage />} />
78+
<Route path="/screens/mfa-enrollment-screen" element={<MultiFactorAuthEnrollmentScreenPage />} />
7579
</Routes>
7680
</FirebaseUIProvider>
7781
</BrowserRouter>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
"use client";
18+
19+
import { MultiFactorAuthEnrollmentScreen } from "@firebase-ui/react";
20+
import { FactorId } from "firebase/auth";
21+
22+
export default function MultiFactorAuthEnrollmentScreenPage() {
23+
return (
24+
<MultiFactorAuthEnrollmentScreen
25+
hints={[FactorId.TOTP, FactorId.PHONE]}
26+
onEnrollment={() => {
27+
console.log("Enrollment successful");
28+
}}
29+
/>
30+
);
31+
}

packages/angular/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"prepare": "pnpm run build",
1919
"build": "pnpm run build:logos && ng-packagr -p ng-package.json",
2020
"build:logos": "tsx generate-logos.ts",
21-
"test": "jest",
21+
"test": "jest --silent",
2222
"publish:tags": "sh -c 'TAG=\"${npm_package_name}@${npm_package_version}\"; git tag --list \"$TAG\" | grep . || git tag \"$TAG\"; git push origin \"$TAG\"'",
2323
"release": "pnpm pack --pack-destination ../../releases/"
2424
},

packages/angular/src/public-api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { isDevMode } from "@angular/core";
18+
import { registerFramework } from "@firebase-ui/core";
19+
1720
export { EmailLinkAuthFormComponent } from "./lib/auth/forms/email-link-auth-form/email-link-auth-form.component";
1821
export { ForgotPasswordAuthFormComponent } from "./lib/auth/forms/forgot-password-auth-form/forgot-password-auth-form.component";
1922
export { PhoneAuthFormComponent } from "./lib/auth/forms/phone-auth-form/phone-auth-form.component";
@@ -44,3 +47,8 @@ export { ContentComponent } from "./lib/components/content/content.component";
4447

4548
// Provider
4649
export * from "./lib/provider";
50+
51+
if (!isDevMode()) {
52+
const pkgJson = require("../package.json");
53+
registerFramework("angular", pkgJson.version);
54+
}

packages/core/src/auth.test.ts

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
signInWithCredential,
1111
signInAnonymously,
1212
signInWithProvider,
13+
signInWithCustomToken,
1314
generateTotpQrCode,
1415
} from "./auth";
1516

@@ -19,6 +20,7 @@ vi.mock("firebase/auth", () => ({
1920
sendPasswordResetEmail: vi.fn(),
2021
sendSignInLinkToEmail: vi.fn(),
2122
signInAnonymously: vi.fn(),
23+
signInWithCustomToken: vi.fn(),
2224
signInWithRedirect: vi.fn(),
2325
isSignInWithEmailLink: vi.fn(),
2426
EmailAuthProvider: {
@@ -49,6 +51,7 @@ import {
4951
sendPasswordResetEmail as _sendPasswordResetEmail,
5052
sendSignInLinkToEmail as _sendSignInLinkToEmail,
5153
signInAnonymously as _signInAnonymously,
54+
signInWithCustomToken as _signInWithCustomToken,
5255
isSignInWithEmailLink as _isSignInWithEmailLink,
5356
UserCredential,
5457
Auth,
@@ -958,14 +961,11 @@ describe("signInAnonymously", () => {
958961

959962
const result = await signInAnonymously(mockUI);
960963

961-
// Verify state management
962964
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
963965

964-
// Verify the Firebase function was called with correct parameters
965966
expect(_signInAnonymously).toHaveBeenCalledWith(mockUI.auth);
966967
expect(_signInAnonymously).toHaveBeenCalledTimes(1);
967968

968-
// Verify the result
969969
expect(result).toEqual(mockUserCredential);
970970
});
971971

@@ -977,10 +977,83 @@ describe("signInAnonymously", () => {
977977

978978
await signInAnonymously(mockUI);
979979

980-
// Verify error handling
981980
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);
982981

983-
// Verify state management still happens
982+
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
983+
});
984+
});
985+
986+
describe("signInWithCustomToken", () => {
987+
beforeEach(() => {
988+
vi.clearAllMocks();
989+
});
990+
991+
it("should update state and call signInWithCustomToken successfully", async () => {
992+
const mockUI = createMockUI();
993+
const customToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
994+
const mockUserCredential = {
995+
user: { uid: "custom-user-uid", email: "user@example.com" },
996+
providerId: "custom",
997+
operationType: "signIn",
998+
} as UserCredential;
999+
1000+
vi.mocked(_signInWithCustomToken).mockResolvedValue(mockUserCredential);
1001+
1002+
const result = await signInWithCustomToken(mockUI, customToken);
1003+
1004+
expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
1005+
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
1006+
1007+
expect(_signInWithCustomToken).toHaveBeenCalledWith(mockUI.auth, customToken);
1008+
expect(_signInWithCustomToken).toHaveBeenCalledTimes(1);
1009+
1010+
expect(result).toEqual(mockUserCredential);
1011+
});
1012+
1013+
it("should call handleFirebaseError if an error is thrown", async () => {
1014+
const mockUI = createMockUI();
1015+
const customToken = "invalid-token";
1016+
const error = new FirebaseError("auth/invalid-custom-token", "Invalid custom token");
1017+
1018+
vi.mocked(_signInWithCustomToken).mockRejectedValue(error);
1019+
1020+
await signInWithCustomToken(mockUI, customToken);
1021+
1022+
expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
1023+
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);
1024+
1025+
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
1026+
});
1027+
1028+
it("should handle network errors", async () => {
1029+
const mockUI = createMockUI();
1030+
const customToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
1031+
const error = new Error("Network error");
1032+
1033+
vi.mocked(_signInWithCustomToken).mockRejectedValue(error);
1034+
1035+
await signInWithCustomToken(mockUI, customToken);
1036+
1037+
// Verify redirect error is cleared even when network error occurs
1038+
expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
1039+
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);
1040+
1041+
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
1042+
});
1043+
1044+
it("should handle expired custom token", async () => {
1045+
const mockUI = createMockUI();
1046+
const customToken = "expired-token";
1047+
const error = new FirebaseError("auth/custom-token-mismatch", "Custom token expired");
1048+
1049+
vi.mocked(_signInWithCustomToken).mockRejectedValue(error);
1050+
1051+
await signInWithCustomToken(mockUI, customToken);
1052+
1053+
// Verify redirect error is cleared even when token is expired
1054+
expect(mockUI.setRedirectError).toHaveBeenCalledWith(undefined);
1055+
expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error);
1056+
9841057
expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]);
9851058
});
9861059
});

0 commit comments

Comments
 (0)