Aller au contenu

Export users from Firebase Authentication

Ce contenu n’est pas encore disponible dans votre langue.

This guide walks through exporting a Firebase Authentication project’s users into the Canonical Migration Format (CMF), checking what came out, and preparing the result for import into another provider.

iamigrate export --source firebase pages through the project’s accounts with the Identity Toolkit API (accounts:batchGet, 1000 users per page). Each account becomes one CMF record.

Firebase hashes passwords with a modified scrypt that depends on two secrets shared by the whole project: a signer key and a salt separator. The API returns each user’s hash and salt, but never those project parameters, and a hash can’t be verified anywhere else without them. You copy them from the Firebase console and pass them to the export (see Step 2).

  • iamigrate installed (see Installation).
  • A Google service account key for the project, with a role that can list users, such as Firebase Authentication Admin. Create one under Project settings → Service accounts → Generate new private key. Alternatively, an OAuth2 access token, for example from gcloud auth print-access-token.
  • Access to the Firebase console, to read the project’s password hash parameters.
Terminal window
export FIREBASE_PROJECT_ID=acme-prod
export GOOGLE_APPLICATION_CREDENTIALS=./service-account.json

--project and --service-account do the same. Without a project ID, the service account’s own project is used. With FIREBASE_ACCESS_TOKEN or --access-token, the project ID is required.

In the Firebase console, open Authentication → Users, choose the menu at the top right of the user list, and select Password hash parameters. It shows four values:

Console valueFlagEnvironment variable
base64_signer_key--hash-signer-keyFIREBASE_HASH_SIGNER_KEY
base64_salt_separator--hash-salt-separatorFIREBASE_HASH_SALT_SEPARATOR
rounds--hash-rounds (default 8)
mem_cost--hash-mem-cost (default 14)
Terminal window
export FIREBASE_HASH_SIGNER_KEY='jxspr8Ki0RYycVU8zykbdLGjFQ3McFUH0uiiTvC8pVMXAn210wjLNmdZJzxUECKbm0QsEmYUSDzZvpjeJ9WmXA=='
export FIREBASE_HASH_SALT_SEPARATOR='Bw=='
Terminal window
iamigrate export --source firebase --out ./export/
exported 1250 users -> export/users.cmf.jsonl.gz (4 skipped)

The command writes:

export/
├── users.cmf.jsonl.gz # one CMF user per exported account
├── manifest.json # counts per hash algorithm, plus skipped accounts
└── mapping.yaml # scaffolded mapping, with no target set yet

The export is a snapshot. Accounts created, and passwords changed, after it aren’t in it, so plan a freeze or a final re-export before cutover.

iamigrate treats every returned hash as Firebase’s own modified scrypt. The API doesn’t say which algorithm produced a given hash, so check a few users by signing in on the target before you rely on the whole export.

Terminal window
cat export/manifest.json
{
"record_count": 1250,
"hash_algorithm_counts": {
"scrypt": 1100
},
"non_portable_mfa_counts": {
"totp": 12
},
"skipped_records": [
{
"source_id": "k3TqYb0eXmP4aN1cZs9uV2wRlHf2",
"reason": "no email, phone number, or sign-in provider (anonymous or custom-token user)"
}
]
}
  • hash_algorithm_counts shows scrypt for every user with a password. Users who only sign in with Google, Apple or a phone number have none.
  • non_portable_mfa_counts counts second factors that can’t move: TOTP enrollments, whose secrets Firebase never returns, and SMS factors whose phone number came back masked.
  • skipped_records lists accounts that aren’t in users.cmf.jsonl.gz, by Firebase uid.
Skip reasonCause
no email, phone number, or sign-in provider (anonymous or custom-token user)The account is an anonymous or custom-token user with nothing to migrate
unrecognized password hash format: ...The hash or salt isn’t valid base64, or the hash parameters you passed are out of range
invalid customAttributes: ...The account’s custom claims aren’t valid JSON
Firebase fieldCMF field
localId (uid)source_id
email / emailVerifiedemails[0], marked primary
phoneNumberphones[0], marked primary and verified, since Firebase only stores verified numbers
displayNameprofile.name
photoUrlprofile.picture
disabledblocked
passwordHash / salt plus the project parameterspassword: scrypt, with params.variant set to firebase, the signer key in key, and params.salt_separator, params.rounds and params.mem_cost
customAttributes (custom claims)app_metadata
mfaInfo with a phone numberan sms MFA factor, portable: true when the number isn’t masked
mfaInfo TOTP enrollmenta totp MFA factor, portable: false

The Firebase uid becomes source_id, and is what import reports refer to.

  • Linked sign-in providers (providerUserInfo, e.g. a linked Google account). Only the email and phone they carry are kept.
  • Timestamps, such as creation and last sign-in date, and the password’s last update.
  • Tenants. Identity Platform multi-tenancy isn’t supported; only the project’s default tenant is read.
  • Anonymous and custom-token users, skipped as above.

mapping.yaml is scaffolded with target: "". Set the target before you validate with it:

Terminal window
iamigrate map --target <target> --mapping ./export/mapping.yaml

Then check the export against the target’s capabilities. No network calls are made:

Terminal window
iamigrate validate --in ./export/users.cmf.jsonl.gz --mapping ./export/mapping.yaml --target <target>

Firebase’s modified scrypt is only understood by Firebase itself. A target that doesn’t list scrypt fails validation for those users, and one that does, such as Auth0, rejects them at import with TRANSLATION_ERROR. To bring such users over anyway, have them reset their password: drop the password from their CMF records before importing, or mark it "portable": false, and they’re created without one and listed under requires_password_reset.

From there, follow the import guide for your target: