Migrar utilizadores de uma app existente
Este documento mostra como migrar utilizadores de uma app existente para a Identity Platform.
Antes de começar
Usar o SDK Admin
O Admin SDK permite-lhe importar utilizadores sem exportar dados do utilizador para CSV ou JSON. Pode importar utilizadores para todos os fornecedores suportados pela Identity Platform, incluindo OAuth, SAML e OIDC.
É possível importar até 1000 utilizadores numa única chamada de API. A operação de importação
está otimizada em função da velocidade e não verifica se existem campos duplicados. A importação de um utilizador
que entre em conflito com um uid
existente substitui o utilizador existente. A importação de um utilizador com qualquer outro campo duplicado (como email
) resulta num utilizador adicional com o mesmo valor.
O SDK Admin tenta carregar toda a lista de utilizadores fornecidos, mesmo quando ocorre um erro específico do utilizador. A operação devolve um resultado com o resumo das importações bem-sucedidas e com falhas. Os detalhes do erro são devolvidos por importação de utilizador com falha.
Importar utilizadores com palavras-passe com hash HMAC
Os algoritmos de hash HMAC incluem HMAC_MD5
, HMAC_SHA1
, HMAC_SHA256
e
HMAC_SHA512
. Tem de fornecer a chave do signatário de hash.
Node.js
getAuth() .importUsers( [ { uid: 'some-uid', email: 'user@example.com', // Must be provided in a byte buffer. passwordHash: Buffer.from('password-hash'), // Must be provided in a byte buffer. passwordSalt: Buffer.from('salt'), }, ], { hash: { algorithm: 'HMAC_SHA256', // Must be provided in a byte buffer. key: Buffer.from('secret'), }, } ) .then((results) => { results.errors.forEach((indexedError) => { console.log(`Error importing user ${indexedError.index}`); }); }) .catch((error) => { console.log('Error importing users :', error); });
Java
try { List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder() .setUid("some-uid") .setEmail("user@example.com") .setPasswordHash("password-hash".getBytes()) .setPasswordSalt("salt".getBytes()) .build()); UserImportOptions options = UserImportOptions.withHash( HmacSha256.builder() .setKey("secret".getBytes()) .build()); UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options); for (ErrorInfo indexedError : result.getErrors()) { System.out.println("Failed to import user: " + indexedError.getReason()); } } catch (FirebaseAuthException e) { System.out.println("Error importing users: " + e.getMessage()); }
Python
users = [ auth.ImportUserRecord( uid='some-uid', email='user@example.com', password_hash=b'password_hash', password_salt=b'salt' ), ] hash_alg = auth.UserImportHash.hmac_sha256(key=b'secret') try: result = auth.import_users(users, hash_alg=hash_alg) for err in result.errors: print('Failed to import user:', err.reason) except exceptions.FirebaseError as error: print('Error importing users:', error)
Ir
users := []*auth.UserToImport{ (&auth.UserToImport{}). UID("some-uid"). Email("user@example.com"). PasswordHash([]byte("password-hash")). PasswordSalt([]byte("salt")), } h := hash.HMACSHA256{ Key: []byte("secret"), } result, err := client.ImportUsers(ctx, users, auth.WithHash(h)) if err != nil { log.Fatalln("Error importing users", err) } for _, e := range result.Errors { log.Println("Failed to import user", e.Reason) }
Importar utilizadores com palavras-passe com hash MD5, SHA e PBKDF
Os algoritmos hash MD5, SHA e PBKDF incluem MD5
, SHA1
, SHA256
,
SHA512
, PBKDF_SHA1
e PBKDF2_SHA256
. Tem de indicar o número de iterações usadas para aplicar hash à palavra-passe (entre 0 e 8192 para MD5
, entre 1 e 8192 para SHA1
, SHA256
e SHA512
, e entre 0 e 120 000 para PBKDF_SHA1
e PBKDF2_SHA256
).
Node.js
getAuth() .importUsers( [ { uid: 'some-uid', email: 'user@example.com', // Must be provided in a byte buffer. passwordHash: Buffer.from('password-hash'), // Must be provided in a byte buffer. passwordSalt: Buffer.from('salt'), }, ], { hash: { algorithm: 'PBKDF2_SHA256', rounds: 100000, }, } ) .then((results) => { results.errors.forEach((indexedError) => { console.log(`Error importing user ${indexedError.index}`); }); }) .catch((error) => { console.log('Error importing users :', error); });
Java
try { List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder() .setUid("some-uid") .setEmail("user@example.com") .setPasswordHash("password-hash".getBytes()) .setPasswordSalt("salt".getBytes()) .build()); UserImportOptions options = UserImportOptions.withHash( Pbkdf2Sha256.builder() .setRounds(100000) .build()); UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options); for (ErrorInfo indexedError : result.getErrors()) { System.out.println("Failed to import user: " + indexedError.getReason()); } } catch (FirebaseAuthException e) { System.out.println("Error importing users: " + e.getMessage()); }
Python
users = [ auth.ImportUserRecord( uid='some-uid', email='user@example.com', password_hash=b'password_hash', password_salt=b'salt' ), ] hash_alg = auth.UserImportHash.pbkdf2_sha256(rounds=100000) try: result = auth.import_users(users, hash_alg=hash_alg) for err in result.errors: print('Failed to import user:', err.reason) except exceptions.FirebaseError as error: print('Error importing users:', error)
Ir
users := []*auth.UserToImport{ (&auth.UserToImport{}). UID("some-uid"). Email("user@example.com"). PasswordHash([]byte("password-hash")). PasswordSalt([]byte("salt")), } h := hash.PBKDF2SHA256{ Rounds: 100000, } result, err := client.ImportUsers(ctx, users, auth.WithHash(h)) if err != nil { log.Fatalln("Error importing users", err) } for _, e := range result.Errors { log.Println("Failed to import user", e.Reason) }
Importar utilizadores com palavras-passe com hash SCRYPT padrão
O SDK de administrador suporta o algoritmo SCRYPT
padrão, bem como uma versão modificada interna. Os seguintes parâmetros são obrigatórios:
memoryCost
: o custo de CPU/memória do algoritmo de hash.parallelization
: A paralelização do algoritmo de hash.blockSize
: O tamanho do bloco (normalmente 8) do algoritmo de hash.derivedKeyLength
: O comprimento da chave derivada do algoritmo de hash.
Node.js
getAuth() .importUsers( [ { uid: 'some-uid', email: 'user@example.com', // Must be provided in a byte buffer. passwordHash: Buffer.from('password-hash'), // Must be provided in a byte buffer. passwordSalt: Buffer.from('salt'), }, ], { hash: { algorithm: 'STANDARD_SCRYPT', memoryCost: 1024, parallelization: 16, blockSize: 8, derivedKeyLength: 64, }, } ) .then((results) => { results.errors.forEach((indexedError) => { console.log(`Error importing user ${indexedError.index}`); }); }) .catch((error) => { console.log('Error importing users :', error); });
Java
try { List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder() .setUid("some-uid") .setEmail("user@example.com") .setPasswordHash("password-hash".getBytes()) .setPasswordSalt("salt".getBytes()) .build()); UserImportOptions options = UserImportOptions.withHash( StandardScrypt.builder() .setMemoryCost(1024) .setParallelization(16) .setBlockSize(8) .setDerivedKeyLength(64) .build()); UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options); for (ErrorInfo indexedError : result.getErrors()) { System.out.println("Failed to import user: " + indexedError.getReason()); } } catch (FirebaseAuthException e) { System.out.println("Error importing users: " + e.getMessage()); }
Python
users = [ auth.ImportUserRecord( uid='some-uid', email='user@example.com', password_hash=b'password_hash', password_salt=b'salt' ), ] hash_alg = auth.UserImportHash.standard_scrypt( memory_cost=1024, parallelization=16, block_size=8, derived_key_length=64) try: result = auth.import_users(users, hash_alg=hash_alg) for err in result.errors: print('Failed to import user:', err.reason) except exceptions.FirebaseError as error: print('Error importing users:', error)
Ir
users := []*auth.UserToImport{ (&auth.UserToImport{}). UID("some-uid"). Email("user@example.com"). PasswordHash([]byte("password-hash")). PasswordSalt([]byte("salt")), } h := hash.StandardScrypt{ MemoryCost: 1024, Parallelization: 16, BlockSize: 8, DerivedKeyLength: 64, } result, err := client.ImportUsers(ctx, users, auth.WithHash(h)) if err != nil { log.Fatalln("Error importing users", err) } for _, e := range result.Errors { log.Println("Failed to import user", e.Reason) }
Importar utilizadores com palavras-passe com hash SCRYPT do Identity Platform
A Identity Platform e o Firebase usam uma
versão modificada
do algoritmo SCRYPT
. Se precisar de migrar utilizadores do Firebase para a Identity Platform ou de um projeto da Identity Platform para outro, precisa dos parâmetros de hash internos.
Para aceder aos parâmetros na Identity Platform:
- Abra a página Utilizadores na Google Cloud consola.
- Clique em Importar utilizadores. São apresentados os parâmetros de hash de palavra-passe.
Para aceder aos parâmetros no Firebase:
Abra o separador Utilizadores na consola do Firebase.
Selecione Parâmetros de hash de palavra-passe no menu pendente no canto superior direito da lista de utilizadores. São apresentados os parâmetros de hash de palavra-passe.
No seu código, tem de fornecer o seguinte:
key
: a chave de assinatura normalmente fornecida na codificaçãobase64
.saltSeparator
: (Opcional) O separador de sal normalmente fornecido nabase64
codificação.rounds
: o número de ciclos usados para aplicar hash às palavras-passe.memoryCost
: o custo de memória necessário para este algoritmo.
Node.js
getAuth() .importUsers( [ { uid: 'some-uid', email: 'user@example.com', // Must be provided in a byte buffer. passwordHash: Buffer.from('base64-password-hash', 'base64'), // Must be provided in a byte buffer. passwordSalt: Buffer.from('base64-salt', 'base64'), }, ], { hash: { algorithm: 'SCRYPT', // All the parameters below can be obtained from the Firebase Console's users section. // Must be provided in a byte buffer. key: Buffer.from('base64-secret', 'base64'), saltSeparator: Buffer.from('base64SaltSeparator', 'base64'), rounds: 8, memoryCost: 14, }, } ) .then((results) => { results.errors.forEach((indexedError) => { console.log(`Error importing user ${indexedError.index}`); }); }) .catch((error) => { console.log('Error importing users :', error); });
Java
try { List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder() .setUid("some-uid") .setEmail("user@example.com") .setPasswordHash(BaseEncoding.base64().decode("password-hash")) .setPasswordSalt(BaseEncoding.base64().decode("salt")) .build()); UserImportOptions options = UserImportOptions.withHash( Scrypt.builder() // All the parameters below can be obtained from the Firebase Console's "Users" // section. Base64 encoded parameters must be decoded into raw bytes. .setKey(BaseEncoding.base64().decode("base64-secret")) .setSaltSeparator(BaseEncoding.base64().decode("base64-salt-separator")) .setRounds(8) .setMemoryCost(14) .build()); UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options); for (ErrorInfo indexedError : result.getErrors()) { System.out.println("Failed to import user: " + indexedError.getReason()); } } catch (FirebaseAuthException e) { System.out.println("Error importing users: " + e.getMessage()); }
Python
users = [ auth.ImportUserRecord( uid='some-uid', email='user@example.com', password_hash=base64.urlsafe_b64decode('password_hash'), password_salt=base64.urlsafe_b64decode('salt') ), ] # All the parameters below can be obtained from the Firebase Console's "Users" # section. Base64 encoded parameters must be decoded into raw bytes. hash_alg = auth.UserImportHash.scrypt( key=base64.b64decode('base64_secret'), salt_separator=base64.b64decode('base64_salt_separator'), rounds=8, memory_cost=14 ) try: result = auth.import_users(users, hash_alg=hash_alg) for err in result.errors: print('Failed to import user:', err.reason) except exceptions.FirebaseError as error: print('Error importing users:', error)
Ir
b64URLdecode := func(s string) []byte { b, err := base64.URLEncoding.DecodeString(s) if err != nil { log.Fatalln("Failed to decode string", err) } return b } b64Stddecode := func(s string) []byte { b, err := base64.StdEncoding.DecodeString(s) if err != nil { log.Fatalln("Failed to decode string", err) } return b } // Users retrieved from Firebase Auth's backend need to be base64URL decoded users := []*auth.UserToImport{ (&auth.UserToImport{}). UID("some-uid"). Email("user@example.com"). PasswordHash(b64URLdecode("password-hash")). PasswordSalt(b64URLdecode("salt")), } // All the parameters below can be obtained from the Firebase Console's "Users" // section. Base64 encoded parameters must be decoded into raw bytes. h := hash.Scrypt{ Key: b64Stddecode("base64-secret"), SaltSeparator: b64Stddecode("base64-salt-separator"), Rounds: 8, MemoryCost: 14, } result, err := client.ImportUsers(ctx, users, auth.WithHash(h)) if err != nil { log.Fatalln("Error importing users", err) } for _, e := range result.Errors { log.Println("Failed to import user", e.Reason) }
Importar utilizadores com palavras-passe com hash BCRYPT
Não são necessários parâmetros adicionais nem salts de palavras-passe para palavras-passe com hash.BCRYPT
Node.js
getAuth() .importUsers( [ { uid: 'some-uid', email: 'user@example.com', // Must be provided in a byte buffer. passwordHash: Buffer.from('password-hash'), }, ], { hash: { algorithm: 'BCRYPT', }, } ) .then((results) => { results.errors.forEach((indexedError) => { console.log(`Error importing user ${indexedError.index}`); }); }) .catch((error) => { console.log('Error importing users :', error); });
Java
try { List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder() .setUid("some-uid") .setEmail("user@example.com") .setPasswordHash("password-hash".getBytes()) .setPasswordSalt("salt".getBytes()) .build()); UserImportOptions options = UserImportOptions.withHash(Bcrypt.getInstance()); UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options); for (ErrorInfo indexedError : result.getErrors()) { System.out.println("Failed to import user: " + indexedError.getReason()); } } catch (FirebaseAuthException e) { System.out.println("Error importing users: " + e.getMessage()); }
Python
users = [ auth.ImportUserRecord( uid='some-uid', email='user@example.com', password_hash=b'password_hash', password_salt=b'salt' ), ] hash_alg = auth.UserImportHash.bcrypt() try: result = auth.import_users(users, hash_alg=hash_alg) for err in result.errors: print('Failed to import user:', err.reason) except exceptions.FirebaseError as error: print('Error importing users:', error)
Ir
users := []*auth.UserToImport{ (&auth.UserToImport{}). UID("some-uid"). Email("user@example.com"). PasswordHash([]byte("password-hash")). PasswordSalt([]byte("salt")), } h := hash.Bcrypt{} result, err := client.ImportUsers(ctx, users, auth.WithHash(h)) if err != nil { log.Fatalln("Error importing users", err) } for _, e := range result.Errors { log.Println("Failed to import user", e.Reason) }
Importar utilizadores sem palavras-passe
Se os seus utilizadores se autenticarem com um fornecedor de identidade externo através do OAuth, SAML ou OIDC, não tem acesso direto à respetiva palavra-passe.
Node.js
getAuth() .importUsers([ { uid: 'some-uid', displayName: 'John Doe', email: 'johndoe@gmail.com', photoURL: 'http://www.example.com/12345678/photo.png', emailVerified: true, phoneNumber: '+11234567890', // Set this user as admin. customClaims: { admin: true }, // User with Google provider. providerData: [ { uid: 'google-uid', email: 'johndoe@gmail.com', displayName: 'John Doe', photoURL: 'http://www.example.com/12345678/photo.png', providerId: 'google.com', }, ], }, ]) .then((results) => { results.errors.forEach((indexedError) => { console.log(`Error importing user ${indexedError.index}`); }); }) .catch((error) => { console.log('Error importing users :', error); });
Java
try { List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder() .setUid("some-uid") .setDisplayName("John Doe") .setEmail("johndoe@gmail.com") .setPhotoUrl("http://www.example.com/12345678/photo.png") .setEmailVerified(true) .setPhoneNumber("+11234567890") .putCustomClaim("admin", true) // set this user as admin .addUserProvider(UserProvider.builder() // user with Google provider .setUid("google-uid") .setEmail("johndoe@gmail.com") .setDisplayName("John Doe") .setPhotoUrl("http://www.example.com/12345678/photo.png") .setProviderId("google.com") .build()) .build()); UserImportResult result = FirebaseAuth.getInstance().importUsers(users); for (ErrorInfo indexedError : result.getErrors()) { System.out.println("Failed to import user: " + indexedError.getReason()); } } catch (FirebaseAuthException e) { System.out.println("Error importing users: " + e.getMessage()); }
Python
users = [ auth.ImportUserRecord( uid='some-uid', display_name='John Doe', email='johndoe@gmail.com', photo_url='http://www.example.com/12345678/photo.png', email_verified=True, phone_number='+11234567890', custom_claims={'admin': True}, # set this user as admin provider_data=[ # user with Google provider auth.UserProvider( uid='google-uid', email='johndoe@gmail.com', display_name='John Doe', photo_url='http://www.example.com/12345678/photo.png', provider_id='google.com' ) ], ), ] try: result = auth.import_users(users) for err in result.errors: print('Failed to import user:', err.reason) except exceptions.FirebaseError as error: print('Error importing users:', error)
Ir
users := []*auth.UserToImport{ (&auth.UserToImport{}). UID("some-uid"). DisplayName("John Doe"). Email("johndoe@gmail.com"). PhotoURL("http://www.example.com/12345678/photo.png"). EmailVerified(true). PhoneNumber("+11234567890"). CustomClaims(map[string]interface{}{"admin": true}). // set this user as admin ProviderData([]*auth.UserProvider{ // user with Google provider { UID: "google-uid", Email: "johndoe@gmail.com", DisplayName: "John Doe", PhotoURL: "http://www.example.com/12345678/photo.png", ProviderID: "google.com", }, }), } result, err := client.ImportUsers(ctx, users) if err != nil { log.Fatalln("Error importing users", err) } for _, e := range result.Errors { log.Println("Failed to import user", e.Reason) }
Tenha em atenção que o providerId
é usado em toda a Identity Platform para descrever um fornecedor específico. Para fornecedores OIDC e SAML, isto é definido durante a criação.
Para outros fornecedores, este tem um valor predefinido (como google.com
ou
facebook.com
). Pode obter o providerId
das reivindicações do utilizador com sessão iniciada.
Importar utilizadores com vários fatores de autenticação
Se os seus utilizadores existentes tiverem números de telefone inscritos para utilização com a autenticação multifator, pode importá-los para a Identity Platform.
Os utilizadores com autenticação multifator têm de ter um email validado e um primeiro fator suportado. São permitidos até 5 fatores secundários por utilizador.
Pode importar as seguintes propriedades multifator:
Propriedade | Tipo | Descrição |
---|---|---|
uid |
de string |
Um ID exclusivo opcional do segundo fator inscrito. Se não for fornecido, é gerado automaticamente um uid aleatório.
|
phoneNumber |
de string | O número de telefone do segundo fator inscrito. Este número de telefone tem de estar em conformidade com a norma E.164. |
displayName |
de string | Um nome a apresentar opcional. Isto é útil se um utilizador tiver vários segundos fatores inscritos. |
enrollmentTime |
de string | A data em que o segundo fator foi inscrito, formatada como uma string UTC. Se não for indicado, é usada a data atual. |
factorId |
de string |
O identificador do tipo de segundo fator. Esta opção está sempre definida como phone .
|
O exemplo seguinte mostra como importar utilizadores com autenticação multifator:
Node.js
// Up to 1000 users can be imported at once.
const userImportRecords = [
{
uid: 'uid1',
email: 'user1@example.com',
emailVerified: true,
passwordHash: Buffer.from('passwordHash1'),
passwordSalt: Buffer.from('salt1'),
multiFactor: {
enrolledFactors: [
{
// Enrolled second factor uid is optional.
uid: 'uid1-unique-mfa-identifier1',
displayName: 'Personal phone',
phoneNumber: '+16505551234',
factorId: 'phone',
// Enrollment time is also optional.
enrollmentTime: 'Fri, 22 Sep 2017 01:49:58 GMT',
},
],
},
},
{
// User with multiple second factors.
uid: 'uid2',
email: 'user2@example.com',
emailVerified: true,
passwordHash: Buffer.from('passwordHash2'),
passwordSalt: Buffer.from('salt2'),
multiFactor: {
enrolledFactors: [
{
displayName: 'Work phone',
phoneNumber: '+16505550007',
factorId: 'phone',
},
{
displayName: 'Backup phone',
phoneNumber: '+16505550008',
factorId: 'phone',
},
],
},
},
{
// User with no second factor.
uid: 'uid3',
email: 'user3@example.com',
passwordHash: Buffer.from('passwordHash3'),
passwordSalt: Buffer.from('salt3'),
},
...
];
const userImportOptions = {
hash: {
algorithm: 'HMAC_SHA256',
key: Buffer.from('secretKey'),
},
};
admin.auth().importUsers(userImportRecords, userImportOptions)
.then((userImportResult) => {
// The number of successful imports is determined via: userImportResult.successCount.
// The number of failed imports is determined via: userImportResult.failureCount.
// To get the error details.
userImportResult.forEach(function(indexedError) {
// The corresponding user that failed to upload.
console.log(userImportRecords[indexedError.index].uid +' failed to import',
indexedError.error);
});
})
.catch((error) => {
// Some unrecoverable error occurred that prevented the operation from running.
});
Usar a interface de linhas de comando
Se os seus dados do utilizador estiverem armazenados como JSON ou CSV, pode importá-los através da ferramenta de linha de comandos do Firebase:
firebase auth:import account_file \
--hash-algo=[HASH-ALGORITHM] \
--hash-key=[KEY] \
--salt-separator=[SALT-SEPARATOR] \
--rounds=[ROUNDS] \
--mem-cost=[MEM-COST] \
--parallelization=[PARALLELIZATION] \
--block-size=[BLOCK-SIZE] \
--dk-len=[DK-LEN] \
--hash-input-order=[HASH-INPUT-ORDER] \
Consulte a documentação de referência da interface de linhas de comando para ver uma descrição completa de cada parâmetro.
O que se segue?
- Consulte os códigos de erro comuns que ocorrem quando importa utilizadores.
- Saiba como autenticar utilizadores no App Engine através da Identity Platform.
- Saiba como configurar reivindicações personalizadas com a Identity Platform.
- Leia acerca da API Admin Auth.