Skip to content

Commit d480154

Browse files
committed
Merge branch '@invertase/v7-development' of https://github.com/firebase/firebaseui-web into @invertase/v7-development
2 parents f06977c + b49a4b0 commit d480154

File tree

1 file changed

+377
-0
lines changed

1 file changed

+377
-0
lines changed

MIGRATION.md

Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
# Migration Guide
2+
3+
## Overview
4+
5+
FirebaseUI for Web has been completely rewritten from the ground up. The previous version (v6) was a single JavaScript package that provided a monolithic authentication UI solution. The new version (v7) represents a modern, modular architecture that separates concerns and provides better flexibility for developers.
6+
7+
### Architecture Changes
8+
9+
**Previous Version (v6):**
10+
- Single JavaScript package (`firebaseui`) that handled both authentication logic and UI rendering
11+
- Tightly coupled to DOM manipulation and jQuery-like patterns
12+
- Limited customization options
13+
- Framework-agnostic but with a rigid structure
14+
15+
**New Version (v7):**
16+
- **Framework-agnostic core package** (`@invertase/firebaseui-core`): Contains all authentication logic, state management, behaviors, and utilities without any UI dependencies
17+
- **Framework-specific packages**: Separate packages for React (`@invertase/firebaseui-react`), Angular (`@invertase/firebaseui-angular`), and Shadcn components
18+
- **Supporting packages**: Separate packages for styles (`@invertase/firebaseui-styles`) and translations (`@invertase/firebaseui-translations`)
19+
- **Composable architecture**: Components are designed to be composed together, allowing for greater flexibility
20+
- **Modern patterns**: Uses reactive stores (nanostores), TypeScript throughout, and modern framework patterns
21+
22+
### Migration Path
23+
24+
**Important:** There is no direct migration path from v6 to v7. This is a complete rewrite with a fundamentally different architecture and API. You cannot simply update the package version and expect your existing code to work.
25+
26+
Instead, you will need to:
27+
1. Remove the old `firebaseui` package
28+
2. Install the appropriate new package(s) for your framework
29+
3. Rewrite your authentication UI components using the new API
30+
4. Update your configuration and styling approach
31+
32+
### What This Guide Covers
33+
34+
This migration guide maps features and concepts from the old [v6 version](https://github.com/firebase/firebaseui-web/tree/v6) to the new v7 rewrite, helping you understand:
35+
- How authentication methods translate between versions
36+
- How configuration options map to the new behaviors system
37+
- How UI customization works in the new architecture
38+
- How to achieve similar functionality with the new component-based approach
39+
40+
## Migrating
41+
42+
### 1. Installing Packages
43+
44+
First, remove the old `firebaseui` package and install the appropriate new package(s) for your framework:
45+
46+
<details>
47+
<summary>React</summary>
48+
49+
Remove the old package:
50+
```bash
51+
npm uninstall firebaseui
52+
```
53+
54+
Install the new React package:
55+
```bash
56+
npm install @invertase/firebaseui-react
57+
```
58+
59+
The package automatically includes the core package as a dependency, so you don't need to install `@invertase/firebaseui-core` separately.
60+
</details>
61+
62+
<details>
63+
<summary>Angular</summary>
64+
65+
Remove the old package:
66+
```bash
67+
npm uninstall firebaseui
68+
```
69+
70+
Install the new Angular package:
71+
```bash
72+
npm install @invertase/firebaseui-angular
73+
```
74+
75+
**Note:** The Angular package requires [AngularFire](https://github.com/angular/angularfire) to be installed and configured first.
76+
</details>
77+
78+
<details>
79+
<summary>Shadcn</summary>
80+
81+
Remove the old package:
82+
```bash
83+
npm uninstall firebaseui
84+
```
85+
86+
Ensure you have [installed and setup](https://ui.shadcn.com/docs/installation) Shadcn in your project first.
87+
88+
Add the Firebase UI registry to your `components.json`:
89+
```json
90+
{
91+
...
92+
"registries": {
93+
"@firebase": "https://fir-ui-shadcn-registry.web.app/r/{name}.json"
94+
}
95+
}
96+
```
97+
98+
Then add components as needed:
99+
```bash
100+
npx shadcn@latest add @firebase/sign-in-auth-screen
101+
```
102+
103+
This will automatically install all required dependencies.
104+
</details>
105+
106+
### 2. Initialization
107+
108+
The initialization process is fundamentally different between v6 and v7.
109+
110+
**Old Way (v6):**
111+
```javascript
112+
// Initialize the FirebaseUI Widget using Firebase.
113+
var ui = new firebaseui.auth.AuthUI(firebase.auth());
114+
115+
// The start method will wait until the DOM is loaded.
116+
ui.start('#firebaseui-auth-container', uiConfig);
117+
```
118+
119+
**New Way (v7):**
120+
121+
<details>
122+
<summary>React (+Shadcn)</summary>
123+
124+
```tsx
125+
import { initializeApp } from 'firebase/app';
126+
import { initializeUI } from '@invertase/firebaseui-core';
127+
import { FirebaseUIProvider } from '@invertase/firebaseui-react';
128+
129+
const app = initializeApp({ ... });
130+
131+
const ui = initializeUI({
132+
app,
133+
// behaviors and other configuration go here
134+
});
135+
136+
function App() {
137+
return (
138+
<FirebaseUIProvider ui={ui}>
139+
{/* Your app components */}
140+
</FirebaseUIProvider>
141+
);
142+
}
143+
```
144+
</details>
145+
146+
<details>
147+
<summary>Angular</summary>
148+
149+
```ts
150+
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
151+
import { initializeUI } from '@invertase/firebaseui-core';
152+
153+
export const appConfig: ApplicationConfig = {
154+
providers: [
155+
provideFirebaseApp(() => initializeApp({ ... })),
156+
provideFirebaseUI((apps) => initializeUI({
157+
app: apps[0],
158+
// behaviors and other configuration go here
159+
})),
160+
]
161+
};
162+
```
163+
</details>
164+
165+
### 3. Configuration Options Migration
166+
167+
The following table maps v6 configuration options to their v7 equivalents:
168+
169+
| v6 Option | Migration Guide |
170+
|----------|------------------|
171+
| `autoUpgradeAnonymousUsers` | **Use the `autoUpgradeAnonymousUsers` behavior.**<br/><br/>Import `autoUpgradeAnonymousUsers` from `@invertase/firebaseui-core` and add it to your behaviors array:<br/>`behaviors: [autoUpgradeAnonymousUsers({ async onUpgrade(ui, oldUserId, credential) { /* handle merge */ } })]`<br/><br/>The `onUpgrade` callback replaces the `signInFailure` callback for handling merge conflicts. |
172+
| `callbacks` | **Use component props/events instead.**<br/><br/>v6 callbacks like `signInSuccessWithAuthResult`, `signInFailure`, etc. are replaced by component event handlers:<br/><br/>**React:** `onSignIn={(user) => { ... }}`, `onSignUp={(user) => { ... }}`, `onForgotPasswordClick={() => { ... }}`<br/><br/>**Angular:** `(signIn)="onSignIn($event)"`, `(signUp)="onSignUp($event)"`, `(forgotPassword)="onForgotPassword()"`<br/><br/>These are passed directly to the components you use, giving you more control over the flow. |
173+
| `credentialHelper` | **Use the `oneTapSignIn` behavior.**<br/><br/>The credential helper (Account Chooser) from v6 is replaced by Google One Tap in v7. Import `oneTapSignIn` from `@invertase/firebaseui-core` and add it to your behaviors array:<br/>`behaviors: [oneTapSignIn({ clientId: '...', autoSelect: false, cancelOnTapOutside: false })]`<br/><br/>**Note:** This requires Google Sign In to be enabled in Firebase Console. Get the `clientId` from "Web SDK configuration" settings. See [Google One Tap documentation](https://developers.google.com/identity/gsi/web/reference/js-reference) for all configuration options. |
174+
| `queryParameterForSignInSuccessUrl` | **Handle in your routing logic.**<br/><br/>v7 doesn't have built-in URL parameter handling. Instead, handle redirects in your `onSignIn` callback by reading URL params:<br/><br/>**React/Angular:** `const urlParams = new URLSearchParams(window.location.search);`<br/>`const redirectUrl = urlParams.get('signInSuccessUrl') || '/dashboard';`<br/>`window.location.href = redirectUrl;`<br/><br/>**Angular (with Router):** Use `ActivatedRoute` to read query params and `Router` to navigate. |
175+
| `queryParameterForWidgetMode` | **Not applicable.**<br/><br/>v7 doesn't use widget modes. Instead, you explicitly render the components you need:<br/><br/>**React:** `<SignInAuthScreen />`, `<SignUpAuthScreen />`<br/><br/>**Angular:** `<fui-sign-in-auth-screen>`, `<fui-sign-up-auth-screen>` |
176+
| `signInFlow` | **Use provider strategy behaviors.**<br/><br/>Replace `signInFlow: 'redirect'` with:<br/>`import { providerRedirectStrategy } from '@invertase/firebaseui-core'`<br/>`behaviors: [providerRedirectStrategy()]`<br/><br/>Replace `signInFlow: 'popup'` with:<br/>`import { providerPopupStrategy } from '@invertase/firebaseui-core'`<br/>`behaviors: [providerPopupStrategy()]`<br/><br/>**Note:** `popup` is the default strategy in v7. |
177+
| `immediateFederatedRedirect` | **Control via component rendering.**<br/><br/>v7 doesn't have this option. Instead, you control whether to show OAuth buttons or redirect immediately by conditionally rendering components:<br/><br/>**React:** `{singleProvider ? <Navigate to="/oauth-redirect" /> : <OAuthScreen onSignIn={handleSignIn} />}`<br/><br/>**Angular:** Use `*ngIf` or `@if` to conditionally render `<fui-oauth-screen>` or use `Router` to navigate directly. |
178+
| `signInOptions` | **Use OAuth button components directly.**<br/><br/>v6's `signInOptions` array is replaced by explicitly rendering the OAuth provider buttons you want:<br/><br/>**React:** Import `GoogleSignInButton`, `FacebookSignInButton`, `AppleSignInButton` from `@invertase/firebaseui-react` and render them inside `<OAuthScreen>`.<br/><br/>**Angular:** Import `GoogleSignInButtonComponent`, `FacebookSignInButtonComponent`, `AppleSignInButtonComponent` from `@invertase/firebaseui-angular` and use selectors `<fui-google-sign-in-button>`, `<fui-facebook-sign-in-button>`, `<fui-apple-sign-in-button>` inside `<fui-oauth-screen>`.<br/><br/>The order you place the buttons determines their display order. |
179+
| `signInSuccessUrl` | **Handle in `onSignIn` callback.**<br/><br/>Instead of a configuration option, handle redirects in your component's `onSignIn` callback:<br/><br/>**React:** `<SignInAuthScreen onSignIn={(user) => { window.location.href = '/dashboard'; }} />`<br/><br/>**Angular:** `<fui-sign-in-auth-screen (signIn)="onSignIn($event)" />` with `onSignIn(user: User) { this.router.navigate(['/dashboard']); }`<br/><br/>*Required in v6 when `signInSuccessWithAuthResult` callback is not used or returns `true`. |
180+
| `tosUrl` | **Pass via `policies` prop.**<br/><br/>**React:** Pass `policies={{ termsOfServiceUrl: 'https://example.com/tos', privacyPolicyUrl: 'https://example.com/privacy' }}` to `<FirebaseUIProvider>`.<br/><br/>**Angular:** Use `provideFirebaseUIPolicies(() => ({ termsOfServiceUrl: '...', privacyPolicyUrl: '...' }))`.<br/><br/>The policies are automatically rendered in auth forms and screens. |
181+
| `privacyPolicyUrl` | **Pass via `policies` prop.**<br/><br/>See `tosUrl` above - both URLs are passed together in the `policies` object. |
182+
| `adminRestrictedOperation` | **Handle in your UI logic.**<br/><br/>v7 doesn't have built-in support for this GCIP-specific feature. You'll need to:<br/>(1) Check if sign-up is disabled in your Firebase project settings<br/>(2) Handle the `auth/admin-restricted-operation` error in your error handling<br/>(3) Display appropriate messaging to users when sign-up attempts are blocked<br/><br/>You can check for this error in your `onSignUp` or form error handlers and display custom UI accordingly. |
183+
184+
### Additional Configuration
185+
186+
#### Configure Phone Provider
187+
188+
In v6, phone authentication country code configuration was handled via the `signInOptions` configuration. In v7, this is controlled by the `countryCodes` behavior.
189+
190+
**v6:**
191+
```javascript
192+
signInOptions: [
193+
{
194+
provider: firebase.auth.PhoneAuthProvider.PROVIDER_ID,
195+
defaultCountry: 'GB',
196+
whitelistedCountries: ['GB', 'US', 'FR']
197+
}
198+
]
199+
```
200+
201+
**v7:**
202+
Use the `countryCodes` behavior to configure allowed countries and default country:
203+
204+
```ts
205+
import { countryCodes } from '@invertase/firebaseui-core';
206+
207+
const ui = initializeUI({
208+
app,
209+
behaviors: [
210+
countryCodes({
211+
allowedCountries: ['GB', 'US', 'FR'], // only allow Great Britain, USA and France
212+
defaultCountry: 'GB', // GB is default
213+
}),
214+
],
215+
});
216+
```
217+
218+
The `countryCodes` behavior affects all phone authentication flows, including regular phone sign-in and multi-factor authentication (MFA) enrollment. The `CountrySelector` component automatically uses these settings to filter and display available countries.
219+
220+
#### Sign In Flows
221+
222+
In v6, you configured the sign-in flow (popup vs redirect) via the `signInFlow` configuration option. In v7, this is controlled by provider strategy behaviors.
223+
224+
**v6:**
225+
```javascript
226+
var uiConfig = {
227+
signInFlow: 'popup', // or 'redirect'
228+
// ...
229+
};
230+
```
231+
232+
**v7:**
233+
Use the `providerPopupStrategy` (default) or `providerRedirectStrategy` behaviors:
234+
235+
```ts
236+
import { providerPopupStrategy, providerRedirectStrategy } from '@invertase/firebaseui-core';
237+
238+
// For popup flow (default)
239+
const ui = initializeUI({
240+
app,
241+
behaviors: [providerPopupStrategy()],
242+
});
243+
244+
// For redirect flow
245+
const ui = initializeUI({
246+
app,
247+
behaviors: [providerRedirectStrategy()],
248+
});
249+
```
250+
251+
**Note:** The popup strategy is the default in v7. If you don't specify a strategy, popup will be used. The strategy applies to all OAuth providers (Google, Facebook, Apple, etc.).
252+
253+
#### Multi-tenancy Support
254+
255+
v7 supports multi-tenancy by allowing you to pass a custom `Auth` instance with a `tenantId` configured to `initializeUI`.
256+
257+
**v6:**
258+
```javascript
259+
var tenantAuth = firebase.auth(app).tenantId = 'tenant-id';
260+
var ui = new firebaseui.auth.AuthUI(tenantAuth);
261+
```
262+
263+
**v7:**
264+
265+
**React:**
266+
```tsx
267+
import { getAuth } from 'firebase/auth';
268+
import { initializeUI } from '@invertase/firebaseui-core';
269+
270+
const auth = getAuth(app);
271+
auth.tenantId = 'tenant-id';
272+
273+
const ui = initializeUI({
274+
app,
275+
auth, // Pass the auth instance with tenantId
276+
});
277+
```
278+
279+
**Angular:**
280+
```ts
281+
import { getAuth } from 'firebase/auth';
282+
import { initializeUI } from '@invertase/firebaseui-core';
283+
284+
export const appConfig: ApplicationConfig = {
285+
providers: [
286+
provideFirebaseApp(() => initializeApp({ ... })),
287+
provideAuth(() => {
288+
const auth = getAuth();
289+
auth.tenantId = 'tenant-id';
290+
return auth;
291+
}),
292+
provideFirebaseUI((apps) => {
293+
const auth = getAuth(apps[0]);
294+
auth.tenantId = 'tenant-id';
295+
return initializeUI({
296+
app: apps[0],
297+
auth,
298+
});
299+
}),
300+
],
301+
};
302+
```
303+
304+
#### Enabling Anonymous User Upgrade
305+
306+
In v6, anonymous user upgrade was configured via the `autoUpgradeAnonymousUsers` option. In v7, this is handled by the `autoUpgradeAnonymousUsers` behavior.
307+
308+
**v6:**
309+
```javascript
310+
var uiConfig = {
311+
autoUpgradeAnonymousUsers: true,
312+
callbacks: {
313+
signInFailure: function(error) {
314+
// Handle merge conflicts
315+
}
316+
}
317+
};
318+
```
319+
320+
**v7:**
321+
Use the `autoUpgradeAnonymousUsers` behavior:
322+
323+
```ts
324+
import { autoUpgradeAnonymousUsers } from '@invertase/firebaseui-core';
325+
326+
const ui = initializeUI({
327+
app,
328+
behaviors: [
329+
autoUpgradeAnonymousUsers({
330+
async onUpgrade(ui, oldUserId, credential) {
331+
// Handle account merge logic
332+
// e.g., migrate data from oldUserId to new user
333+
console.log(`Upgrading anonymous user ${oldUserId} to ${credential.user.uid}`);
334+
},
335+
}),
336+
],
337+
});
338+
```
339+
340+
The behavior automatically upgrades anonymous users when they sign in with any credential (email/password, OAuth, phone, etc.). The `onUpgrade` callback is optional and allows you to perform custom logic during the upgrade, such as migrating user data.
341+
342+
#### Handling Anonymous User Upgrade Merge Conflicts
343+
344+
In v6, merge conflicts (when an account already exists with the same credential) were handled via the `signInFailure` callback. In v7, the upgrade process handles this differently.
345+
346+
**How it works in v7:**
347+
348+
When an anonymous user attempts to sign in with a credential that's already associated with an existing account, Firebase Auth will automatically link the anonymous account to the existing account. The upgrade process:
349+
350+
1. **Stores the anonymous user ID** in `localStorage` before redirect flows (for OAuth redirects)
351+
2. **Automatically links** the anonymous account to the existing account
352+
3. **Calls the `onUpgrade` callback** (if provided) with both the old anonymous user ID and the new credential
353+
4. **Cleans up** the stored anonymous user ID from `localStorage`
354+
355+
**Example:**
356+
```ts
357+
const ui = initializeUI({
358+
app,
359+
behaviors: [
360+
autoUpgradeAnonymousUsers({
361+
async onUpgrade(ui, oldUserId, credential) {
362+
// oldUserId is the anonymous user's ID
363+
// credential.user.uid is the existing account's ID after linking
364+
365+
// Migrate any data from the anonymous account
366+
await migrateUserData(oldUserId, credential.user.uid);
367+
368+
// The anonymous account is now linked to the existing account
369+
// The user is signed in with their existing account
370+
},
371+
}),
372+
],
373+
});
374+
```
375+
376+
**Note:** If a merge conflict occurs and the linking fails (e.g., due to account linking restrictions), Firebase Auth will throw an error that you can handle in your error handling logic. The `onUpgrade` callback will only be called if the upgrade is successful.
377+

0 commit comments

Comments
 (0)