From e8d1a9c5044f40150060b39c3d357074974821d2 Mon Sep 17 00:00:00 2001 From: visha raut Date: Wed, 24 Jun 2026 08:46:43 +0530 Subject: [PATCH] fix: support exporting and importing mfaInfo in auth ### Description Fixes auth:export and auth:import silently discarding MFA user data (mfaInfo) by explicitly including the 'mfaInfo' key in the allowed JSON extraction and validation fields. ### Scenarios Tested - JSON auth export correctly contains mfaInfo array (verified with unit tests). - JSON auth import successfully parses and validates mfaInfo (verified with unit tests). ### Sample Commands firebase auth:export accounts.json --format=json Fixes #10704 --- CHANGELOG.md | 1 + src/accountExporter.spec.ts | 33 +++++++++++++++++++++++++++++++++ src/accountExporter.ts | 2 +- src/accountImporter.spec.ts | 17 +++++++++++++++++ src/accountImporter.ts | 2 +- 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7175238eba3..0ec7a6eef84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Fixes `auth:export` and `auth:import` dropping `mfaInfo` data for users with Multi-Factor Authentication enabled. - Upgrade `zod` to v4 and drop the deprecated `zod-to-json-schema` dependency in favor of zod v4's built-in `z.toJSONSchema()`. - Updated the Firebase Data Connect local toolkit to v3.4.14, which includes the following changes: - Fix linter warnings in generated Kotlin SDK files. diff --git a/src/accountExporter.spec.ts b/src/accountExporter.spec.ts index 4d25b00b92e..08d1899ae34 100644 --- a/src/accountExporter.spec.ts +++ b/src/accountExporter.spec.ts @@ -35,6 +35,7 @@ describe("accountExporter", () => { displayName: string; disabled: boolean; customAttributes?: string; + mfaInfo?: any[]; providerUserInfo?: { providerId: string; rawId: string; @@ -311,6 +312,38 @@ describe("accountExporter", () => { expect(nock.isDone()).to.be.true; }); + it("should export a user's mfaInfo for JSON formats", async () => { + userList[0].mfaInfo = [ + { + mfaEnrollmentId: "enrollment-id-1", + displayName: "My SMS MFA", + phoneInfo: "+11111111111", + enrolledAt: "2026-06-24T00:00:00Z", + }, + ]; + nock("https://www.googleapis.com") + .post("/identitytoolkit/v3/relyingparty/downloadAccount", { + maxResults: 3, + targetProjectId: "test-project-id", + }) + .reply(200, { + users: userList.slice(0, 3), + }); + await serialExportUsers("test-project-id", { + format: "JSON", + batchSize: 3, + writeStream: writeStream, + }); + expect(spyWrite.getCall(0).args[0]).to.eq(JSON.stringify(userList[0], null, 2)); + expect(spyWrite.getCall(1).args[0]).to.eq( + "," + os.EOL + JSON.stringify(userList[1], null, 2), + ); + expect(spyWrite.getCall(2).args[0]).to.eq( + "," + os.EOL + JSON.stringify(userList[2], null, 2), + ); + expect(nock.isDone()).to.be.true; + }); + function mockAllUsersRequests(): void { nock("https://www.googleapis.com") .post("/identitytoolkit/v3/relyingparty/downloadAccount", { diff --git a/src/accountExporter.ts b/src/accountExporter.ts index 91965bf778d..22c07a7e307 100644 --- a/src/accountExporter.ts +++ b/src/accountExporter.ts @@ -11,7 +11,6 @@ const apiClient = new Client({ urlPrefix: googleOrigin(), }); -// TODO: support for MFA at runtime was added in PR #3173, but this exporter currently ignores `mfaInfo` and loses the data on export. const EXPORTED_JSON_KEYS = [ "localId", "email", @@ -25,6 +24,7 @@ const EXPORTED_JSON_KEYS = [ "phoneNumber", "disabled", "customAttributes", + "mfaInfo", ]; const EXPORTED_JSON_KEYS_RENAMING: Record = { lastLoginAt: "lastSignedInAt", diff --git a/src/accountImporter.spec.ts b/src/accountImporter.spec.ts index 2965278499e..fa20befc181 100644 --- a/src/accountImporter.spec.ts +++ b/src/accountImporter.spec.ts @@ -148,6 +148,23 @@ describe("accountImporter", () => { }), ).to.not.have.property("error"); }); + + it("should not reject when mfaInfo is present in user json", () => { + expect( + validateUserJson({ + localId: "123", + email: "test@test.org", + mfaInfo: [ + { + mfaEnrollmentId: "enrollment-id-1", + displayName: "My SMS MFA", + phoneInfo: "+11111111111", + enrolledAt: "2026-06-24T00:00:00Z", + }, + ], + }), + ).to.not.have.property("error"); + }); }); describe("serialImportUsers", () => { diff --git a/src/accountImporter.ts b/src/accountImporter.ts index 39a9e741387..16515e3427b 100644 --- a/src/accountImporter.ts +++ b/src/accountImporter.ts @@ -10,7 +10,6 @@ const apiClient = new Client({ urlPrefix: googleOrigin(), }); -// TODO: support for MFA at runtime was added in PR #3173, but this importer currently ignores `mfaInfo` and loses the data on import. const ALLOWED_JSON_KEYS = [ "localId", "email", @@ -25,6 +24,7 @@ const ALLOWED_JSON_KEYS = [ "phoneNumber", "disabled", "customAttributes", + "mfaInfo", ]; const ALLOWED_JSON_KEYS_RENAMING = { lastSignedInAt: "lastLoginAt",