Gestione programmatica dei tenant di Identity Platform

Questo documento spiega come utilizzare l'SDK Admin di Identity Platform per gestire i tenant e i relativi utenti in modo programmatico. Ecco alcune attività che puoi svolgere come amministratore:

  • Gestione utenti: crea, aggiorna, elimina ed elenca gli utenti per un tenant specifico.

  • Verifica dell'identità:identifica gli utenti di un'app per limitare l'accesso alle risorse sul tuo server.

  • Importa utenti:esegui la migrazione degli utenti da un sistema di autenticazione esterno o da un altro progetto o tenant Identity Platform.

  • Controllo dell'accesso con rivendicazioni personalizzate:definisci attributi personalizzati negli account utente per un tenant specifico e implementa varie strategie di controllo dell'accesso dell'accesso, come controllo dell'accesso basato sui ruoli.

  • Gestione delle sessioni utente:revoca i token di aggiornamento di un utente per un tenant specifico.

  • Link di azioni via email:genera link email personalizzati per la reimpostazione della password, l'accesso tramite link email e la verifica email per gli utenti di un tenant specifico.

  • Gestione dei tenant: crea, elenca, recupera, aggiorna ed elimina i tenant per un progetto Identity Platform specifico.

  • Gestisci i provider OIDC e SAML nei tenant: gestisci in modo programmatico le configurazioni OIDC e SAML in un tenant specificato.

Prima di iniziare

Funzionalità supportate

La tabella seguente elenca le funzionalità supportate da ciascun SDK in un ambiente multi-tenant:

Funzionalità Node.js Java Python Vai C#
Emissione di token personalizzati
Verifica dei token ID
Gestire gli utenti
Controllo dell'accesso con rivendicazioni personalizzate
Revocare i token di aggiornamento
Importazione degli utenti
Generazione di link di azioni via email
Autenticazione a più fattori
Gestione delle configurazioni dei provider SAML/OIDC
Gestione dei cookie di sessione

La tabella seguente mostra i metodi di accesso che puoi configurare utilizzando l'SDK Admin e la console Google Cloud in un contesto specifico del tenant:

Funzionalità Console Google Cloud SDK Admin
Email
OIDC
SAML
Social
Telefono
Autenticazione a più fattori
Anonimo

Gestione dei tenant

Con l'SDK Admin, puoi gestire i tenant in modo programmatico da un ambiente server sicuro anziché utilizzare la console Google Cloud. Ciò include la possibilità di creare, elencare, recuperare, modificare o eliminare gli tenant.

Ogni tenant contiene i propri provider di identità, impostazioni e set di utenti. Le operazioni CRUD (Create, Read, Update, Delete) di gestione della configurazione del tenant sono disponibili dall'istanza del progetto principale utilizzando admin.auth().tenantManager().

Una configurazione del tenant fornisce informazioni su un tenant, ad esempio il nome visualizzato, l'identificatore del tenant e la configurazione dell'autenticazione email.

Tutte le altre impostazioni (ad esempio i domini nella lista consentita e gli URI di reindirizzamento autenticati) di un tenant vengono ereditate dal progetto principale. Questi devono essere gestiti tramite la console Google Cloud.

Per operazioni come la gestione degli utenti specifici del tenant, la configurazione dei fornitori OIDC/SAML e la generazione di link email, è necessaria un'istanza TenantAwareAuth per il tenant di destinazione (identificato dal relativo tenantId univoco).

Node.js

const tenantManager = admin.auth().tenantManager();
const tenantAuth = tenantManager.authForTenant(tenantId);

Python

from firebase_admin import tenant_mgt

tenant_client = tenant_mgt.auth_for_tenant(tenant_id)

Java

FirebaseAuth auth = FirebaseAuth.getInstance();
TenantManager tenantManager = auth.getTenantManager();
TenantAwareFirebaseAuth tenantAuth = tenantManager.getAuthForTenant(tenantId);

Tutte le chiamate alle API di gestione degli utenti, alle API di gestione dei provider OIDC/SAML e alle API di generazione di link email rientrano nell'ambito di questo tenant (utilizzando la sua istanza TenantAwareAuth).

Ottenere un tenant esistente

L'SDK Admin fornisce il metodo getTenant(), che recupera le informazioni su un tenant in base al suo tenantId (un identificatore univoco per il tenant).

Node.js

admin.auth().tenantManager().getTenant(tenantId)
  .then((tenant) => {
    console.log(tenant.toJSON());
  })
  .catch((error) => {
    // Handle error.
  });

Python

tenant = tenant_mgt.get_tenant(tenant_id)

print('Retreieved tenant:', tenant.tenant_id)

Java

Tenant tenant = FirebaseAuth.getInstance().getTenantManager().getTenant(tenantId);
System.out.println("Retrieved tenant: " + tenant.getTenantId());

Questo metodo restituisce un oggetto Tenant corrispondente a tenantId. Se il valore tenantId fornito non appartiene a un tenant esistente, la promessa restituita viene rifiutata con un errore auth/tenant-not-found.

Fai attenzione a non confondere un'istanza Tenant con un oggetto TenantAwareAuth. authInstance.tenantManager().authForTenant() restituisce un'istanza TenantAwareAuth che estende BaseAuth. La classe Auth estende anche BaseAuth. BaseAuth fornisce API per gestire gli utenti e configurare i provider OIDC/SAML in contesti diversi. Per Auth, il contesto è a livello di progetto principale. Per TenantAwareAuth, il contesto è a livello di tenant (il tenant è determinato dall'ID tenant). Il metodo getTenant() risolverà con informazioni di base sul tenant (come ID tenant, nome visualizzato e impostazioni del provider email), ma per chiamare le API in quel tenant devi utilizzare authForTenant(tenantFromGetTenant.tenantId).

Creazione di un tenant

Utilizza il metodo createTenant() per creare una nuova configurazione del tenant:

Node.js

admin.auth().tenantManager().createTenant({
  displayName: 'myTenant1',
  emailSignInConfig: {
    enabled: true,
    passwordRequired: false, // Email link sign-in enabled.
  },
  // TODO: Remove if you don't want to enable multi-factor authentication.
  multiFactorConfig: {
    state: 'ENABLED',
    factorIds: ['phone']
  },
  // TODO: Remove if you don't want to register test phone numbers for use
  // with multi-factor authentication.
  testPhoneNumbers: {
    '+16505551234': '145678',
    '+16505550000': '123456'
  },
})
.then((createdTenant) => {
  console.log(createdTenant.toJSON());
})
.catch((error) => {
  // Handle error.
});

Python

tenant = tenant_mgt.create_tenant(
    display_name='myTenant1',
    enable_email_link_sign_in=True,
    allow_password_sign_up=True)

print('Created tenant:', tenant.tenant_id)

Java

Tenant.CreateRequest request = new Tenant.CreateRequest()
    .setDisplayName("myTenant1")
    .setEmailLinkSignInEnabled(true)
    .setPasswordSignInAllowed(true);
Tenant tenant = FirebaseAuth.getInstance().getTenantManager().createTenant(request);
System.out.println("Created tenant: " + tenant.getTenantId());

Puoi fornire qualsiasi combinazione di queste proprietà:

Proprietà Tipo Descrizione
displayName
string
    
Il nome visualizzato del tenant. Deve contenere da 4 a 20 caratteri, costituiti da lettere, cifre e trattini e deve iniziare con una lettera.
emailSignInConfig
{
  enable: boolean,
  passwordRequired: boolean
}
    
La configurazione del provider di accesso via email. Ad esempio, se il provider email è attivo e se è richiesta una password per accedere all'email. Se non è richiesto, l'accesso con email può essere eseguito con password o utilizzando l'accesso con link email.
multiFactorConfig
{
  state: 'DISABLED' | 'ENABLED',
  factorIds: string[]
}
    
Se l'autenticazione a più fattori è abilitata per il tenant e quali tipi di fattori sono consentiti. Al momento, l'unico ID fattore supportato è phone.
testPhoneNumbers
{
  string: string
}
  
Una mappa dei numeri di telefono e dei relativi codici di autenticazione a più fattori da registrare per scopi di test. Sono consentite al massimo 10 voci. Per rimuovere tutti i numeri di telefono di test, imposta questo campo su null.

Il metodo restituisce un oggetto Tenant per il tenant appena creato.

Aggiornamento di un tenant

Utilizza il metodo updateTenant() per modificare i dati di un tenant esistente. Dovrai specificare tenantId, insieme alle proprietà da aggiornare per il tenant.

Node.js

admin.auth().tenantManager().updateTenant(tenantId, {
  displayName: 'updatedName',
  emailSignInConfig: {
    enabled: false, // Disable email provider.
  },
  // Enable multi-factor authentication.
  multiFactorConfig: {
    state: 'ENABLED',
    factorIds: ['phone']
  },
  // Register phone numbers for testing.
  testPhoneNumbers: {
    '+16505551234': '145678',
    '+16505550000': '123456'
  },
})
.then((updatedTenant) => {
  console.log(updatedTenant.toJSON());
})
.catch((error) => {
  // Handle error.
});

Python

tenant = tenant_mgt.update_tenant(
    tenant_id,
    display_name='updatedName',
    allow_password_sign_up=False) # Disable email provider

print('Updated tenant:', tenant.tenant_id)

Java

Tenant.UpdateRequest request = new Tenant.UpdateRequest(tenantId)
    .setDisplayName("updatedName")
    .setPasswordSignInAllowed(false);
Tenant tenant = FirebaseAuth.getInstance().getTenantManager().updateTenant(request);
System.out.println("Updated tenant: " + tenant.getTenantId());

updateTenant() accetta le stesse proprietà di createTenant(). Tutte le proprietà sono facoltative. Se non viene specificata una proprietà, il valore esistente non verrà modificato.

Al termine, il metodo restituisce un oggetto Tenant aggiornato. Se il valore fornito per tenantId non appartiene a un tenant esistente, la promessa restituita viene rifiutata con un errore auth/tenant-not-found.

Eliminazione di un tenant

Puoi eliminare un tenant utilizzando il relativo tenantId:

Node.js

admin.auth().tenantManager().deleteTenant(tenantId)
  .then(() => {
    // Tenant deleted.
  })
  .catch((error) => {
    // Handle error.
  });

Python

tenant_mgt.delete_tenant(tenant_id)

Java

FirebaseAuth.getInstance().getTenantManager().deleteTenant(tenantId);

Il metodo restituisce un risultato vuoto quando l'eliminazione viene completata correttamente. Se il valore tenantId fornito non appartiene a un tenant esistente, la promessa restituita viene rifiutata con un errore auth/tenant-not-found.

Tenant delle schede

Utilizza il metodo listTenants() per elencare gli utenti esistenti:

Node.js

function listAllTenants(nextPageToken) {
  return admin.auth().tenantManager().listTenants(100, nextPageToken)
    .then((result) => {
      result.tenants.forEach((tenant) => {
        console.log(tenant.toJSON());
      });
      if (result.pageToken) {
        return listAllTenants(result.pageToken);
      }
    });
}

listAllTenants();

Python

for tenant in tenant_mgt.list_tenants().iterate_all():
    print('Retrieved tenant:', tenant.tenant_id)

Java

ListTenantsPage page = FirebaseAuth.getInstance().getTenantManager().listTenants(null);
for (Tenant tenant : page.iterateAll()) {
  System.out.println("Retrieved tenant: " + tenant.getTenantId());
}

Ogni batch di risultati contiene un elenco di tenant, oltre a un token di pagina successiva per elencare il batch successivo di tenant. Quando tutti gli utenti sono già stati elencati, non viene restituito alcun pageToken.

Se non viene specificato alcun campo maxResults, il valore predefinito è 1000 tenant per batch. Si tratta anche del numero massimo di inquilini che possono essere elencati contemporaneamente. Qualsiasi valore maggiore del massimo genera un errore di argomento. Se non viene specificato pageToken, il metodo elencherà gli tenant dall'inizio.

Gestione programmatica dei provider SAML e OIDC

L'SDK Admin fornisce API per gestire in modo programmatico le configurazioni dei provider Security Assertion Markup Language (SAML) 2.0 e OpenID Connect (OIDC) da un ambiente server sicuro.

Con l'SDK Admin, puoi gestire questi provider per un tenant specifico. Questa operazione è simile alla gestione dei provider OIDC e SAML a livello di progetto.

Per gestire i provider per un tenant, crea prima un'istanza TenantAwareAuth:

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

Puoi quindi eseguire operazioni comuni, come creare, modificare o eliminare i provider per un tenant.

Creazione di un provider

Il codice seguente mostra come creare un provider SAML per un tenant:

Node.js

const newConfig = {
  displayName: 'SAML provider name',
  enabled: true,
  providerId: 'saml.myProvider',
  idpEntityId: 'IDP_ENTITY_ID',
  ssoURL: 'https://example.com/saml/sso/1234/',
  x509Certificates: [
    '-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----',
    '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----'
  ],
  rpEntityId: 'RP_ENTITY_ID',
  // Using the default callback URL.
  callbackURL: 'https://project-id.firebaseapp.com/__/auth/handler'
};

tenantAuth.createProviderConfig(newConfig).then(() => {
  // Successful creation.
}).catch((error) => {
  // Handle error.
});

Python

saml = tenant_client.create_saml_provider_config(
    display_name='SAML provider name',
    enabled=True,
    provider_id='saml.myProvider',
    idp_entity_id='IDP_ENTITY_ID',
    sso_url='https://example.com/saml/sso/1234/',
    x509_certificates=[
        '-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----',
        '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----',
    ],
    rp_entity_id='P_ENTITY_ID',
    callback_url='https://project-id.firebaseapp.com/__/auth/handler')

print('Created new SAML provider:', saml.provider_id)

Java

SamlProviderConfig.CreateRequest request = new SamlProviderConfig.CreateRequest()
    .setDisplayName("SAML provider name")
    .setEnabled(true)
    .setProviderId("saml.myProvider")
    .setIdpEntityId("IDP_ENTITY_ID")
    .setSsoUrl("https://example.com/saml/sso/1234/")
    .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT1...\n-----END CERTIFICATE-----")
    .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----")
    .setRpEntityId("RP_ENTITY_ID")
    .setCallbackUrl("https://project-id.firebaseapp.com/__/auth/handler");
SamlProviderConfig saml = tenantAuth.createSamlProviderConfig(request);
System.out.println("Created new SAML provider: " + saml.getProviderId());

Modificare un fornitore

Il seguente codice mostra come modificare un fornitore:

Node.js

const updatedConfig = {
  x509Certificates: [
    '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----',
    '-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----',
  ],
};
tenantAuth.updateProviderConfig('saml.myProvider', updatedConfig).then(() => {
  // Successful update.
}).catch((error) => {
  // Handle error.
});

Python

saml = tenant_client.update_saml_provider_config(
    'saml.myProvider',
    x509_certificates=[
        '-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----',
        '-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----',
    ])

print('Updated SAML provider:', saml.provider_id)

Java

SamlProviderConfig.UpdateRequest request =
    new SamlProviderConfig.UpdateRequest("saml.myProvider")
      .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT2...\n-----END CERTIFICATE-----")
      .addX509Certificate("-----BEGIN CERTIFICATE-----\nCERT3...\n-----END CERTIFICATE-----");
SamlProviderConfig saml = tenantAuth.updateSamlProviderConfig(request);
System.out.println("Updated SAML provider: " + saml.getProviderId());

Trovare un fornitore

Il seguente codice mostra come recuperare la configurazione del provider per un tenant specifico utilizzando il relativo ID provider:

Node.js

tenantAuth.getProviderConfig('saml.myProvider').then((config) => {
  // Get display name and whether it is enabled.
  console.log(config.displayName, config.enabled);
}).catch((error) => {
  // Handle error. Common error is that config is not found.
});

Python

saml = tennat_client.get_saml_provider_config('saml.myProvider')
print(saml.display_name, saml.enabled)

Java

SamlProviderConfig saml = tenantAuth.getSamlProviderConfig("saml.myProvider");

// Get display name and whether it is enabled.
System.out.println(saml.getDisplayName() + " " + saml.isEnabled());

Provider di schede

Il seguente codice mostra come elencare le configurazioni del provider per un determinato tenant:

Node.js

// Returns 10 SAML provider configs starting from the specified nextPageToken offset.
tenantAuth.listProviderConfigs({type: 'saml', maxResults: 10, pageToken: 'nextPageToken'}).then((results) => {
  results.providerConfigs.forEach((config) => {
    console.log(config.providerId);
  });
  // To list the next 10:
  // return tenantAuth.listProviderConfigs(
  //     {type: 'saml', maxResults: 10, pageToken: results.pageToken});
}).catch((error) => {
  // Handle error.
});

Python

for saml in tenant_client.list_saml_provider_configs('nextPageToken').iterate_all():
    print(saml.provider_id)

Java

ListProviderConfigsPage<SamlProviderConfig> page = tenantAuth.listSamlProviderConfigs(
    "nextPageToken");
for (SamlProviderConfig saml : page.iterateAll()) {
  System.out.println(saml.getProviderId());
}

Eliminare un fornitore

Il seguente codice mostra come eliminare un fornitore:

Node.js

tenantAuth.deleteProviderConfig('saml.myProvider').then(() => {
  // Successful deletion.
}).catch((error) => {
  // Handle error.
});

Python

tenant_client.delete_saml_provider_config('saml.myProvider')

Java

tenantAuth.deleteSamlProviderConfig("saml.myProvider");

I provider OIDC vengono gestiti in modo simile ai provider OIDC a livello di progetto, tranne per il fatto che possono essere gestiti dall'istanza TenantAwareAuth corrispondente anziché da un'istanza Auth a livello di progetto.

Per scoprire di più, consulta Gestione programmatica dei provider SAML e OIDC.

Gestire gli utenti specifici del tenant

Puoi utilizzare l'SDK Admin per creare, recuperare, aggiornare, eliminare ed elencare tutti gli utenti di un tenant specifico.

Per iniziare, devi avere un'istanza TenantAwareAuth per il tenant corrispondente:

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

Ottenere un utente

Puoi recuperare un utente specifico del tenant con un identificatore uid:

Node.js

tenantAuth.getUser(uid)
  .then((userRecord) => {
    // See the UserRecord reference documentation to learn more.
    console.log('Successfully fetched user data:', userRecord.toJSON());
    // Tenant ID will be reflected in userRecord.tenantId.
  })
  .catch((error) => {
    console.log('Error fetching user data:', error);
  });

Python

	# Get an auth.Client from tenant_mgt.auth_for_tenant()
    user = tenant_client.get_user(uid)
    print('Successfully fetched user data:', user.uid)

Java

// Get an auth client from the firebase.App
UserRecord user = tenantAuth.getUser(uid);
System.out.println("Successfully fetched user data: " + user.getDisplayName());

Puoi anche identificare un utente tramite il suo indirizzo email:

Node.js

tenantAuth.getUserByEmail(email)
  .then((userRecord) => {
    // See the UserRecord reference documentation to learn more.
    console.log('Successfully fetched user data:', userRecord.toJSON());
    // Tenant ID will be reflected in userRecord.tenantId.
  })
  .catch((error) => {
    console.log('Error fetching user data:', error);
  });

Python

user = tenant_client.get_user_by_email(email)
print('Successfully fetched user data:', user.uid)

Java

// Get an auth client from the firebase.App
UserRecord user = tenantAuth.getUserByEmail(email);
System.out.println("Successfully fetched user data: " + user.getDisplayName());

Creazione di un utente

Utilizza il metodo createUser() per creare nuovi utenti per un tenant specifico. Quando crei un nuovo utente, fornire un uid è facoltativo. Se non specificato, Identity Platform ne eseguirà il provisioning in modo univoco.

Node.js

tenantAuth.createUser({
  email: 'user@example.com',
  emailVerified: false,
  phoneNumber: '+11234567890',
  password: 'secretPassword',
  displayName: 'John Doe',
  photoURL: 'http://www.example.com/12345678/photo.png',
  disabled: false
})
.then((userRecord) => {
  // See the UserRecord reference documentation to learn more.
  console.log('Successfully created new user:', userRecord.uid);
  // Tenant ID will be reflected in userRecord.tenantId.
})
.catch((error) => {
  console.log('Error creating new user:', error);
});

Python

user = tenant_client.create_user(
    email='user@example.com',
    email_verified=False,
    phone_number='+15555550100',
    password='secretPassword',
    display_name='John Doe',
    photo_url='http://www.example.com/12345678/photo.png',
    disabled=False)
print('Sucessfully created new user:', user.uid)

Java

UserRecord.CreateRequest request = new UserRecord.CreateRequest()
    .setEmail("user@example.com")
    .setEmailVerified(false)
    .setPhoneNumber("+15555550100")
    .setPassword("secretPassword")
    .setDisplayName("John Doe")
    .setPhotoUrl("http://www.example.com/12345678/photo.png")
    .setDisabled(false);
UserRecord user = tenantAuth.createUser(request);
System.out.println("Successfully created user: " + user.getDisplayName());

Modificare un utente

Puoi modificare gli utenti esistenti specificando il loro uid al metodo updateUser():

Node.js

tenantAuth.updateUser(uid, {
  email: 'modifiedUser@example.com',
  phoneNumber: '+11234567890',
  emailVerified: true,
  password: 'newPassword',
  displayName: 'Jane Doe',
  photoURL: 'http://www.example.com/12345678/photo.png',
  disabled: true
})
.then((userRecord) => {
  // See the UserRecord reference documentation to learn more.
  console.log('Successfully updated user', userRecord.toJSON());
})
.catch((error) => {
  console.log('Error updating user:', error);
});

Python

user = tenant_client.update_user(
    uid,
    email='user@example.com',
    phone_number='+15555550100',
    email_verified=True,
    password='newPassword',
    display_name='John Doe',
    photo_url='http://www.example.com/12345678/photo.png',
    disabled=True)
print('Sucessfully updated user:', user.uid)

Java

UserRecord.UpdateRequest request = new UserRecord.UpdateRequest(uid)
    .setEmail("user@example.com")
    .setEmailVerified(true)
    .setPhoneNumber("+15555550100")
    .setPassword("newPassword")
    .setDisplayName("John Doe")
    .setPhotoUrl("http://www.example.com/12345678/photo.png")
    .setDisabled(true);
UserRecord user = tenantAuth.updateUser(request);
System.out.println("Successfully updated user: " + user.getDisplayName());

Eliminare un utente

L'esempio seguente mostra come eliminare un utente in base al suo uid:

Node.js

tenantAuth.deleteUser(uid)
  .then(() => {
    console.log('Successfully deleted user');
  })
  .catch((error) => {
    console.log('Error deleting user:', error);
  });

Python

tenant_client.delete_user(uid)
print('Successfully deleted user')

Java

tenantAuth.deleteUser(uid);

System.out.println("Successfully deleted user: " + uid);

Elenco utenti

Per recuperare un intero elenco di utenti per un tenant specifico in batch, utilizza il metodo listUsers(). Ogni batch conterrà un elenco di record utente, oltre a un token pagina successiva se rimangono altri utenti.

Node.js

function listAllUsers(nextPageToken) {
  // List batch of users, 1000 at a time.
  tenantAuth.listUsers(1000, nextPageToken)
    .then((listUsersResult) => {
      listUsersResult.users.forEach((userRecord) => {
        console.log('user', userRecord.toJSON());
        // Tenant ID will be reflected in userRecord.tenantId.
      });
      if (listUsersResult.pageToken) {
        // List next batch of users.
        listAllUsers(listUsersResult.pageToken);
      }
    })
    .catch((error) => {
      console.log('Error listing users:', error);
    });
}
// Start listing users from the beginning, 1000 at a time.
listAllUsers();

Python

	# Note, behind the scenes, the iterator will retrive 1000 users at a time through the API
    for user in tenant_client.list_users().iterate_all():
        print('User: ' + user.uid)

	# Iterating by pages of 1000 users at a time.
    page = tenant_client.list_users()
    while page:
        for user in page.users:
            print('User: ' + user.uid)
        # Get next batch of users.
        page = page.get_next_page()

Java

// Note, behind the scenes, the ListUsersPage retrieves 1000 Users at a time
// through the API
ListUsersPage  page = tenantAuth.listUsers(null);
for (ExportedUserRecord user : page.iterateAll()) {
  System.out.println("User: " + user.getUid());
}

// Iterating by pages 100 users at a time.
page = tenantAuth.listUsers(null, 100);
while (page != null) {
  for (ExportedUserRecord user : page.getValues()) {
    System.out.println("User: " + user.getUid());
  }

  page = page.getNextPage();
}

Per saperne di più, consulta la documentazione dell'SDK Admin sulla gestione degli utenti.

Importazione degli utenti

Puoi utilizzare l'SDK Admin per importare collettivamente gli utenti in un tenant specifico con privilegi elevati. Ciò offre numerosi vantaggi, come la possibilità di eseguire la migrazione degli utenti da un altro prodotto Identity Platform, da un altro tenant o da un sistema di autenticazione esterno che utilizza un algoritmo di hashing diverso. Puoi anche importare gli utenti con provider federati (come SAML e OIDC) e rivendicazioni personalizzate direttamente in blocco.

Per iniziare, ottieni un'istanza TenantAwareAuth per il tenant corrispondente:

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

Puoi importare fino a 1000 utenti alla volta utilizzando un algoritmo di hashing specifico.

Node.js

tenantAuth.importUsers([{
  uid: 'uid1',
  email: 'user1@example.com',
  // Must be provided in a byte buffer.
  passwordHash: Buffer.from('password-hash-1'),
  // Must be provided in a byte buffer.
  passwordSalt: Buffer.from('salt1')
},
{
  uid: 'uid2',
  email: 'user2@example.com',
  // Must be provided in a byte buffer.
  passwordHash: Buffer.from('password-hash-2'),
  // Must be provided in a byte buffer.
  passwordSalt: Buffer.from('salt2')

}], {
  hash: {
    algorithm: 'HMAC_SHA256',
    // Must be provided in a byte buffer.
    key: Buffer.from('secret')
  }
})
.then((results) => {
  results.errors.forEach(function(indexedError) {
  console.log('Error importing user ' + indexedError.index);
  });
})
.catch((error) => {
  console.log('Error importing users:', error);
});

Python

users = [
    auth.ImportUserRecord(
        uid='uid1',
        email='user1@example.com',
        password_hash=b'password_hash_1',
        password_salt=b'salt1'
    ),
    auth.ImportUserRecord(
        uid='uid2',
        email='user2@example.com',
        password_hash=b'password_hash_2',
        password_salt=b'salt2'
    ),
]

hash_alg = auth.UserImportHash.hmac_sha256(key=b'secret')
try:
    result = tenant_client.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)

Java

List<ImportUserRecord> users = new ArrayList<>();
users.add(ImportUserRecord.builder()
    .setUid("uid1")
    .setEmail("user1@example.com")
    .setPasswordHash("password-hash-1".getBytes())
    .setPasswordSalt("salt1".getBytes())
    .build());
users.add(ImportUserRecord.builder()
    .setUid("uid2")
    .setEmail("user2@example.com")
    .setPasswordHash("password-hash-2".getBytes())
    .setPasswordSalt("salt2".getBytes())
    .build());
UserImportHash hmacSha256 = HmacSha256.builder()
    .setKey("secret".getBytes())
    .build();
UserImportResult result = tenantAuth.importUsers(users, UserImportOptions.withHash(hmacSha256));

for (ErrorInfo error : result.getErrors()) {
  System.out.println("Failed to import user: " + error.getReason());
}

Per tutti gli utenti importati, il valore tenantId sarà impostato su tenantAuth.tenantId.

Gli utenti senza password possono essere importati anche in un tenant specifico. Questi utenti possono essere importati con provider federati e rivendicazioni personalizzate.

Node.js

tenantAuth.importUsers([{
  uid: 'some-uid',
  displayName: 'John Doe',
  email: 'johndoe@acme.com',
  photoURL: 'http://www.example.com/12345678/photo.png',
  emailVerified: true,
  phoneNumber: '+11234567890',
  // Set this user as admin.
  customClaims: {admin: true},
  // User with SAML provider.
  providerData: [{
    uid: 'saml-uid',
    email: 'johndoe@acme.com',
    displayName: 'John Doe',
    photoURL: 'http://www.example.com/12345678/photo.png',
    providerId: 'saml.acme'
  }]
}])
.then(function(results) {
  results.errors.forEach(function(indexedError) {
  console.log('Error importing user ' + indexedError.index);
  });
})
.catch(function(error) {
  console.log('Error importing users:', error);
});

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 SAML provider
            auth.UserProvider(
                uid='saml-uid',
                email='johndoe@gmail.com',
                display_name='John Doe',
                photo_url='http://www.example.com/12345678/photo.png',
                provider_id='saml.acme'
            )
        ],
    ),
]
try:
    result = tenant_client.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)

Java

List<ImportUserRecord> users = new ArrayList<>();
users.add(ImportUserRecord.builder()
    .setUid("some-uid")
    .setDisplayName("John Doe")
    .setEmail("johndoe@acme.com")
    .setPhotoUrl("https://www.example.com/12345678/photo.png")
    .setEmailVerified(true)
    .setPhoneNumber("+11234567890")
    // Set this user as admin.
    .putCustomClaim("admin", true)
    // User with SAML provider.
    .addUserProvider(UserProvider.builder()
        .setUid("saml-uid")
        .setEmail("johndoe@acme.com")
        .setDisplayName("John Doe")
        .setPhotoUrl("https://www.example.com/12345678/photo.png")
        .setProviderId("saml.acme")
        .build())
    .build());

UserImportResult result = tenantAuth.importUsers(users);

for (ErrorInfo error : result.getErrors()) {
  System.out.println("Failed to import user: " + error.getReason());
}

Per saperne di più, consulta la sezione Importare gli utenti nella documentazione dell'SDK Admin.

Verifica dell'identità

Quando un'app client Identity Platform comunica con un server di backend personalizzato, l'utente attualmente connesso deve essere identificato su quel server. Questa operazione può essere eseguita in modo sicuro inviando il token ID dell'utente dopo un accesso riuscito utilizzando una connessione sicura al server. Il server può quindi verificare l'integrità e l'autenticità del token ID.

L'SDK Admin dispone di un metodo integrato per verificare e decodificare i token ID per un tenant specifico.

Dopo aver eseguito l'accesso di un utente a un tenant specifico dal client, recupera il token ID dell'utente utilizzando l'SDK client:

auth.tenantId = 'TENANT-ID';
auth.signInWithEmailAndPassword('user@example.com', 'password')
  .then((userCredential) => {
    return userCredential.user.getIdToken();
  })
  .then((idToken) => {
    // Send the ID token to server for verification. ID token should be scoped to TENANT-ID.
  });

Crea un'istanza di TenantAwareAuth sul server:

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

Puoi quindi verificare il token ID per quel tenant specifico:

Node.js

// idToken comes from the client app
tenantAuth.verifyIdToken(idToken)
  .then((decodedToken) => {
    let uid = decodedToken.uid;
    // This should be set to TENANT-ID. Otherwise auth/mismatching-tenant-id error thrown.
    console.log(decodedToken.firebase.tenant);
    // ...
  }).catch((error) => {
    // Handle error
  });

Una risorsa lato server potrebbe essere accessibile a più tenant con diversi livelli di accesso. Poiché in questo caso l'ID tenant potrebbe non essere noto in anticipo, il token ID può essere verificato innanzitutto a livello di progetto.

admin.auth().verifyIdToken(idToken)
  .then((decodedToken) => {
    if (decodedToken.firebase.tenant === 'TENANT-ID1') {
      // Allow appropriate level of access for TENANT-ID1.
    } else if (decodedToken.firebase.tenant === 'TENANT-ID2') {
      // Allow appropriate level of access for TENANT-ID2.
    } else {
      // Block access for all other tenants.
      throw new Error('Access not allowed.');
    }
  }).catch((error) => {
    // Handle error
  });

Python

	# id_token comes from the client app
    try:
        decoded_token = tenant_client.verify_id_token(id_token)

        # This should be set to TENANT-ID. Otherwise TenantIdMismatchError error raised.
        print('Verified ID token from tenant:', decoded_token['firebase']['tenant'])
    except tenant_mgt.TenantIdMismatchError:
        # Token revoked, inform the user to reauthenticate or signOut().
        pass

Java

try {
  // idToken comes from the client app
  FirebaseToken token = tenantAuth.verifyIdToken(idToken);
  // TenantId on the FirebaseToken should be set to TENANT-ID.
  // Otherwise "tenant-id-mismatch" error thrown.
  System.out.println("Verified ID token from tenant: " + token.getTenantId());
} catch (FirebaseAuthException e) {
  System.out.println("error verifying ID token: " + e.getMessage());
}

Per scoprire di più, consulta la documentazione dell'SDK Admin sulla verifica degli token ID.

Gestione delle sessioni utente

Le sessioni di Identity Platform sono di lunga durata. Ogni volta che un utente accede, le sue credenziali vengono verificate sul server di Identity Platform e poi scambiate con un token ID di breve durata e un token di aggiornamento di lunga durata. I token identificativi durano un'ora. I token di aggiornamento non scadono mai, tranne quando un utente viene disattivato, eliminato o sottoposto a una modifica sostanziale dell'account (ad esempio un aggiornamento dell'email o della password).

In alcuni casi, il token di aggiornamento di un utente potrebbe dover essere revocato per motivi di sicurezza, ad esempio se l'utente segnala un dispositivo smarrito o rubato, se viene scoperta una vulnerabilità generale all'interno di un'app o se si verifica una fuga di token attivi su larga scala. L'Admin SDK fornisce un'API per revocare tutti i token di aggiornamento emessi per un utente specifico di un tenant specifico.

Per iniziare, devi disporre di un'istanza TenantAwareAuth:

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

I token di aggiornamento possono essere revocati specificando il uid dell'utente:

Node.js

// Revoke all refresh tokens for a specified user in a specified tenant for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
tenantAuth.revokeRefreshTokens(uid)
  .then(() => {
    return tenantAuth.getUser(uid);
  })
  .then((userRecord) => {
    return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
  })
  .then((timestamp) => {
    console.log('Tokens revoked at: ', timestamp);
  });

Python

	# Revoke all refresh tokens for a specified user in a specified tenant for whatever reason.
	# Retrieve the timestamp of the revocation, in seconds since the epoch.
    tenant_client.revoke_refresh_tokens(uid)

    user = tenant_client.get_user(uid)
    # Convert to seconds as the auth_time in the token claims is in seconds.
    revocation_second = user.tokens_valid_after_timestamp / 1000
    print('Tokens revoked at: {0}'.format(revocation_second))

Java

// Revoke all refresh tokens for a specified user in a specified tenant for whatever reason.
// Retrieve the timestamp of the revocation, in seconds since the epoch.
tenantAuth.revokeRefreshTokens(uid);

// accessing the user's TokenValidAfter
UserRecord user = tenantAuth.getUser(uid);


long timestamp = user.getTokensValidAfterTimestamp() / 1000;
System.out.println("the refresh tokens were revoked at: " + timestamp + " (UTC seconds)");

Una volta revocati i token di aggiornamento, non è possibile emettere nuovi token ID per l'utente fino a quando non eseguirà nuovamente l'autenticazione. Tuttavia, gli ID token esistenti rimarranno attivi fino alla loro scadenza naturale (un'ora).

Puoi verificare che un token ID valido non scaduto non sia revocato specificando il parametro facoltativo checkRevoked. Questo controlla se un token viene revocato dopo la verifica della sua integrità e autenticità.

Node.js

// Verify the ID token for a specific tenant while checking if the token is revoked by passing
// checkRevoked true.
let checkRevoked = true;
tenantAuth.verifyIdToken(idToken, checkRevoked)
  .then(payload => {
    // Token is valid.
  })
  .catch(error => {
    if (error.code == 'auth/id-token-revoked') {
      // Token has been revoked. Inform the user to re-authenticate or
      // signOut() the user.
    } else {
      // Token is invalid.
    }
  });

Python

	# Verify the ID token for a specific tenant while checking if the token is revoked.
    try:
        # Verify the ID token while checking if the token is revoked by
        # passing check_revoked=True.
        decoded_token = tenant_client.verify_id_token(id_token, check_revoked=True)
        # Token is valid and not revoked.
        uid = decoded_token['uid']
    except tenant_mgt.TenantIdMismatchError:
        # Token belongs to a different tenant.
        pass
    except auth.RevokedIdTokenError:
        # Token revoked, inform the user to reauthenticate or signOut().
        pass
    except auth.UserDisabledError:
        # Token belongs to a disabled user record.
        pass
    except auth.InvalidIdTokenError:
        # Token is invalid
        pass

Java

// Verify the ID token for a specific tenant while checking if the token is revoked.
boolean checkRevoked = true;
try {
  FirebaseToken token = tenantAuth.verifyIdToken(idToken, checkRevoked);
  System.out.println("Verified ID token for: " + token.getUid());
} catch (FirebaseAuthException e) {
  if ("id-token-revoked".equals(e.getErrorCode())) {
    // Token is revoked. Inform the user to re-authenticate or signOut() the user.
  } else {
    // Token is invalid
  }
}

Per scoprire di più, consulta la documentazione dell'SDK Admin sulla gestione delle sessioni.

Controllo dell'accesso con rivendicazioni personalizzate

L'Admin SDK supporta la definizione di attributi personalizzati negli account utente per un tenant specifico. Questi attributi ti consentono di implementare diverse strategie di controllo degli accessi, come controllo dell'accesso basato su ruoli. Gli attributi possono essere utilizzati per assegnare agli utenti diversi livelli di accesso applicati dalle regole di sicurezza dell'applicazione.

Per iniziare, ottieni un'istanza TenantAwareAuth per il tenant corrispondente:

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

I claim personalizzati possono contenere dati sensibili, pertanto devono essere impostati solo da un ambiente di server privilegiato utilizzando l'SDK Admin.

Node.js

// Set admin privilege on the user corresponding to uid for a specific tenant.
tenantAuth.setCustomUserClaims(uid, {admin: true}).then(() => {
  // The new custom claims will propagate to the user's ID token the
  // next time a new one is issued.
});

Python

# Set admin privilege on the user corresponding to uid.
tenant_client.set_custom_user_claims(uid, {'admin': True})
# The new custom claims will propagate to the user's ID token the
# next time a new one is issued.

Java

// Set admin privilege on the user corresponding to uid in a specific tenant.
Map<String, Object> claims = new HashMap<>();
claims.put("admin", true);
tenantAuth.setCustomUserClaims(uid, claims);
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.

Gli attributi personalizzati appena impostati verranno visualizzati negli attributi di primo livello del payload del token la volta successiva che l'utente accede o aggiorna i token ID in una sessione esistente. Nell'esempio precedente, il token ID contiene un'affermazione aggiuntiva: {admin: true}.

Dopo aver verificato il token ID e decodificato il relativo payload, è possibile controllare i claim personalizzati aggiuntivi per applicare controllo dell'accesso#39;accesso.

Node.js

// Verify the ID token first.
tenantAuth.verifyIdToken(idToken).then((claims) => {
  if (claims.admin === true) {
    // Allow access to requested admin resource.
  }
});

Python

# Verify the ID token first.
claims = tenant_client.verify_id_token(id_token)
if claims['admin'] is True:
    # Allow access to requested admin resource.
    pass

Java

// Verify the ID token first.
FirebaseToken token = tenantAuth.verifyIdToken(idToken);
if (Boolean.TRUE.equals(token.getClaims().get("admin"))) {
  //Allow access to requested admin resource.
}
// Verify the ID token first.
FirebaseToken decoded = tenantAuth.verifyIdToken(idToken);
if (Boolean.TRUE.equals(decoded.getClaims().get("admin"))) {
  // Allow access to requested admin resource.
}

Le attestazioni personalizzate per un utente esistente per un tenant specifico sono disponibili anche come proprietà nel record utente.

Node.js

// Lookup the user associated with the specified uid.
tenantAuth.getUser(uid).then((userRecord) => {
  // The claims can be accessed on the user record.
  console.log(userRecord.customClaims.admin);
});

Python

	# Lookup the user associated with the specified uid.
    user = tenant_client.get_user(uid)

	# The claims can be accessed on the user record.
    print(user.custom_claims.get('admin'))

Java

// Lookup the user associated with the specified uid in a specific tenant.
UserRecord user = tenantAuth.getUser(uid);
System.out.println(user.getCustomClaims().get("admin"));

Per scoprire di più, consulta la documentazione dell'SDK Admin relativa ai richiesti personalizzati.

Utilizzando gli SDK client di Identity Platform, puoi inviare agli utenti di un tenant specifico email contenenti link che possono utilizzare per la reimpostazione delle password, la verifica dell'indirizzo email e l'accesso tramite email. Queste email vengono inviate da Google e hanno una personalizzazione limitata.

Con l'SDK Admin, puoi generare questi link in modo programmatico nell'ambito di un tenant specifico.

Per iniziare, ottieni un'istanza TenantAwareAuth per il tenant corrispondente:

Node.js

const tenantAuth = admin.auth().tenantManager().authForTenant('TENANT-ID');

Python

tenant_client = tenant_mgt.auth_for_tenant('TENANT-ID')

Java

TenantAwareFirebaseAuth tenantAuth = FirebaseAuth.getInstance().getTenantManager()
    .getAuthForTenant("TENANT-ID");

Il seguente esempio mostra come generare un link per verificare l'indirizzo email di un utente per un tenant specificato:

Node.js

const actionCodeSettings = {
  // URL you want to redirect back to. The domain (www.example.com) for
  // this URL must be whitelisted in the Cloud console.
  url: 'https://www.example.com/checkout?cartId=1234',
  // This must be true for email link sign-in.
  handleCodeInApp: true,
  iOS: {
    bundleId: 'com.example.ios'
  },
  android: {
    packageName: 'com.example.android',
    installApp: true,
    minimumVersion: '12'
  },
  // FDL custom domain.
  dynamicLinkDomain: 'coolapp.page.link'
};

// Admin SDK API to generate the email verification link.
const userEmail = 'user@example.com';
tenantAuth.generateEmailVerificationLink(userEmail, actionCodeSettings)
  .then((link) => {
    // Construct email verification template, embed the link and send
    // using custom SMTP server.
    return sendCustomVerificationEmail(userEmail, displayName, link);
  })
  .catch((error) => {
    // Some error occurred.
  });

Python

action_code_settings = auth.ActionCodeSettings(
    url='https://www.example.com/checkout?cartId=1234',
    handle_code_in_app=True,
    ios_bundle_id='com.example.ios',
    android_package_name='com.example.android',
    android_install_app=True,
    android_minimum_version='12',
    # FDL custom domain.
    dynamic_link_domain='coolapp.page.link',
)

email = 'user@example.com'
link = tenant_client.generate_email_verification_link(email, action_code_settings)
# Construct email from a template embedding the link, and send
# using a custom SMTP server.
send_custom_email(email, link)

Java

ActionCodeSettings actionCodeSettings = ActionCodeSettings.builder()
    // URL you want to redirect back to. The domain (www.example.com) for
    // this URL must be whitelisted in the GCP Console.
    .setUrl("https://www.example.com/checkout?cartId=1234")
    // This must be true for email link sign-in.
    .setHandleCodeInApp(true)
    .setIosBundleId("com.example.ios")
    .setAndroidPackageName("com.example.android")
    .setAndroidInstallApp(true)
    .setAndroidMinimumVersion("12")
    // FDL custom domain.
    .setDynamicLinkDomain("coolapp.page.link")
    .build();

String link = tenantAuth.generateEmailVerificationLink(email, actionCodeSettings);

// Construct email verification template, embed the link and send
// using custom SMTP server.
sendCustomEmail(email, displayName, link);

Sono disponibili meccanismi simili per la generazione di link di reimpostazione della password e di accesso basati su email. Tieni presente che, quando generi un link per un'azione email in un contesto del tenant, l'ID tenant deve essere analizzato dal link e impostato sull'istanza Auth del client prima che il codice possa essere applicato.

const actionCodeUrl = firebase.auth.ActionCodeURL.parseLink(window.location.href);
// A one-time code, used to identify and verify a request.
const code = actionCodeUrl.code;
// The tenant ID being used to trigger the email action.
const tenantId = actionCodeUrl.tenantId;
auth.tenantId = tenantId;

// Apply the action code.
auth.applyActionCode(actionCode)
  .then(() => {
    // User's email is now verified.
  })
  .catch((error) => {
    // Handle error.
  });

Per scoprire di più, consulta la sezione Link alle azioni email nella documentazione dell'SDK Admin.

Messaggi di errore

Nella tabella seguente sono riportati i messaggi di errore comuni che potresti visualizzare.

Codice di errore Descrizione e passaggi per la risoluzione
auth/billing-not-enabled Questa funzionalità richiede l'attivazione della fatturazione.
auth/invalid-display-name Il campo displayName deve essere una stringa valida.
auth/invalid-name Il nome della risorsa fornito non è valido.
auth/invalid-page-token Il token pagina deve essere una stringa non vuota valida.
auth/invalid-project-id Progetto principale non valido. Il progetto principale non ha attivato o non ha attivato la multitenancy.
auth/invalid-tenant-id L'ID tenant deve essere una stringa valida non vuota.
auth/mismatching-tenant-id L'ID tenant dell'utente non corrisponde all'ID tenant TenantAwareAuth corrente.
auth/missing-display-name Alla risorsa in fase di creazione o modifica manca un nome visualizzato valido.
auth/insufficient-permission L'utente non dispone di autorizzazioni sufficienti per accedere alla risorsa richiesta o per eseguire l'operazione specifica del tenant.
auth/quota-exceeded La quota del progetto per l'operazione specificata è stata superata.
auth/tenant-not-found Non esiste alcun tenant corrispondente all'identificatore fornito.
auth/unsupported-tenant-operation Questa operazione non è supportata in un contesto multi-tenant.
auth/invalid-testing-phone-number È stato fornito un numero di telefono di test o un codice di test non valido.
auth/test-phone-number-limit-exceeded È stato superato il numero massimo di coppie di codici e numeri di telefono di test consentiti.