diff --git a/README.md b/README.md
index 1c5b45120..40d8ecfb9 100644
--- a/README.md
+++ b/README.md
@@ -395,6 +395,57 @@ const ui = initializeUI({
});
```
+#### `legacyFetchSignInWithEmail`
+
+The `legacyFetchSignInWithEmail` behavior augments OAuth `auth/account-exists-with-different-credential` flows by calling `fetchSignInMethodsForEmail(auth, email)` and storing the returned methods on the UI instance. In the packaged React and Angular screen components, this recovery state is rendered automatically as a modal on `SignInAuthScreen` and `OAuthScreen`.
+
+The original pending credential is still preserved, so after the user signs in with the correct method, Firebase UI can continue the existing linking flow.
+
+```ts
+import { legacyFetchSignInWithEmail } from '@firebase-oss/ui-core';
+
+const ui = initializeUI({
+ app,
+ behaviors: [legacyFetchSignInWithEmail()],
+});
+```
+
+If you want full control over the UI, hide the built-in recovery component on the screen and read the recovery state directly with `useLegacySignInRecovery()`:
+
+```tsx
+import { GitHubSignInButton, GoogleSignInButton, SignInAuthScreen, useLegacySignInRecovery } from '@firebase-oss/ui-react';
+
+function WrongProviderRecovery() {
+ const { recovery, clearRecovery } = useLegacySignInRecovery();
+
+ if (!recovery) {
+ return null;
+ }
+
+ return (
+
+
You have previously signed in with a different method for {recovery.email}.
+ );
+}
+
+export function CustomSignInScreen() {
+ return (
+
+
+
+ );
+}
+```
+
+Angular apps can hide the built-in recovery UI with `showLegacySignInRecovery="false"` and read the same state with `injectLegacySignInRecovery()` / `injectClearLegacySignInRecovery()`.
+
#### `oneTapSignIn`
The `oneTapSignIn` behavior triggers the [Google One Tap](https://developers.google.com/identity/gsi/web/guides/features) experience to render.
@@ -1061,6 +1112,7 @@ By default, any missing translations will fallback to English if not specified.
| onSignIn | `(user: User) => void?` | Callback when sign-in succeeds |
| onForgotPasswordClick | `() => void?` | Callback when forgot password link is clicked |
| onSignUpClick | `() => void?` | Callback when sign-up link is clicked |
+| showLegacySignInRecovery | `boolean?` | Whether to show the built-in legacy sign-in recovery UI |
**`SignUpAuthScreen`**
@@ -1113,6 +1165,7 @@ By default, any missing translations will fallback to English if not specified.
|------|:----:|-------------|
| onSignIn | `(user: User) => void?` | Callback when sign-in succeeds |
| children | `React.ReactNode?` | Child components |
+| showLegacySignInRecovery | `boolean?` | Whether to show the built-in legacy sign-in recovery UI |
**`OAuthButton`**
@@ -1189,6 +1242,10 @@ By default, any missing translations will fallback to English if not specified.
| asChild | `boolean?` | Render as child component using Slot |
| ...props | `ComponentProps<"button">` | Standard button HTML attributes |
+ **`LegacySignInRecovery`**
+
+ Default component for displaying suggested previous sign-in methods from `legacyFetchSignInWithEmail`.
+
**`Card`**
Card container component.
@@ -1251,6 +1308,12 @@ By default, any missing translations will fallback to English if not specified.
Returns `string | undefined`.
+ **`useLegacySignInRecovery`**
+
+ Gets the legacy sign-in recovery state populated by `legacyFetchSignInWithEmail`.
+
+ Returns `{ recovery: LegacySignInRecovery | undefined; clearRecovery: () => void }`.
+
**`useSignInAuthFormSchema`**
Creates a Zod schema for sign-in form validation.
@@ -1700,6 +1763,10 @@ By default, any missing translations will fallback to English if not specified.
Screen component for email/password sign-in.
+ | Input | Type | Description |
+ |-------|:----:|-------------|
+ | showLegacySignInRecovery | `boolean` | Whether to show the built-in legacy sign-in recovery UI |
+
| Output | Type | Description |
|--------|:----:|-------------|
| signIn | `EventEmitter` | Emitted when sign-in succeeds |
@@ -1754,6 +1821,10 @@ By default, any missing translations will fallback to English if not specified.
Screen component for OAuth provider sign-in.
+ | Input | Type | Description |
+ |-------|:----:|-------------|
+ | showLegacySignInRecovery | `boolean` | Whether to show the built-in legacy sign-in recovery UI |
+
| Output | Type | Description |
|--------|:----:|-------------|
| onSignIn | `EventEmitter` | Emitted when OAuth sign-in succeeds |
@@ -1904,6 +1975,12 @@ By default, any missing translations will fallback to English if not specified.
Component that displays redirect errors from Firebase UI authentication flow.
+ **`LegacySignInRecoveryComponent`**
+
+ Selector: `fui-legacy-sign-in-recovery`
+
+ Default component for displaying suggested previous sign-in methods from `legacyFetchSignInWithEmail`.
+
**`ContentComponent`**
Selector: `fui-content`
@@ -1922,6 +1999,18 @@ By default, any missing translations will fallback to English if not specified.
Returns `Signal`.
+ **`injectLegacySignInRecovery`**
+
+ Injects the legacy sign-in recovery state from the UI store as a signal.
+
+ Returns `Signal`.
+
+ **`injectClearLegacySignInRecovery`**
+
+ Injects a callback that clears the current legacy sign-in recovery state.
+
+ Returns `() => void`.
+
**`injectTranslation`**
Injects a translated string for a given category and key.
diff --git a/examples/react/src/firebase/firebase.ts b/examples/react/src/firebase/firebase.ts
index e0132cda3..ac1996822 100644
--- a/examples/react/src/firebase/firebase.ts
+++ b/examples/react/src/firebase/firebase.ts
@@ -16,7 +16,7 @@
"use client";
-import { countryCodes, initializeUI, oneTapSignIn } from "@firebase-oss/ui-core";
+import { countryCodes, initializeUI, legacyFetchSignInWithEmail, oneTapSignIn } from "@firebase-oss/ui-core";
import { getApps, initializeApp } from "firebase/app";
import { connectAuthEmulator, getAuth } from "firebase/auth";
@@ -30,6 +30,7 @@ export const ui = initializeUI({
app: firebaseApp,
behaviors: [
// autoAnonymousLogin(),
+ legacyFetchSignInWithEmail(),
oneTapSignIn({
clientId: "616577669988-led6l3rqek9ckn9t1unj4l8l67070fhp.apps.googleusercontent.com",
}),
diff --git a/examples/react/src/routes.ts b/examples/react/src/routes.ts
index f46f72903..8bd3e3edd 100644
--- a/examples/react/src/routes.ts
+++ b/examples/react/src/routes.ts
@@ -1,6 +1,7 @@
import SignInAuthScreenPage from "./screens/sign-in-auth-screen";
import SignInAuthScreenWithHandlersPage from "./screens/sign-in-auth-screen-w-handlers";
import SignInAuthScreenWithOAuthPage from "./screens/sign-in-auth-screen-w-oauth";
+import LegacyRecoveryDemoPage from "./screens/legacy-recovery-demo";
import SignUpAuthScreenPage from "./screens/sign-up-auth-screen";
import SignUpAuthScreenWithHandlersPage from "./screens/sign-up-auth-screen-w-handlers";
import SignUpAuthScreenWithOAuthPage from "./screens/sign-up-auth-screen-w-oauth";
@@ -32,6 +33,12 @@ export const routes = [
path: "/screens/sign-in-auth-screen-w-oauth",
component: SignInAuthScreenWithOAuthPage,
},
+ {
+ name: "Legacy Recovery Demo",
+ description: "Use this screen to test wrong-provider recovery for email/password and OAuth attempts.",
+ path: "/screens/legacy-recovery-demo",
+ component: LegacyRecoveryDemoPage,
+ },
{
name: "Sign Up Screen",
description: "A sign up screen with email and password.",
diff --git a/examples/react/src/screens/legacy-recovery-demo.tsx b/examples/react/src/screens/legacy-recovery-demo.tsx
new file mode 100644
index 000000000..004dd4d8b
--- /dev/null
+++ b/examples/react/src/screens/legacy-recovery-demo.tsx
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ AppleSignInButton,
+ FacebookSignInButton,
+ GitHubSignInButton,
+ GoogleSignInButton,
+ MicrosoftSignInButton,
+ SignInAuthScreen,
+ TwitterSignInButton,
+ YahooSignInButton,
+} from "@firebase-oss/ui-react";
+import { useNavigate } from "react-router";
+
+export default function LegacyRecoveryDemoPage() {
+ const navigate = useNavigate();
+
+ return (
+
+
+
Legacy recovery demo
+
Use this screen to test wrong-provider recovery with both email/password and OAuth attempts.
+
+ Suggested flow: create an account with Google first, sign out, then come back here and try the same email with
+ with email/password or another provider like GitHub.
+