Gerir inquilinos da Identity Platform através de programação

Este documento explica como usar o SDK de administrador da Identity Platform para gerir inquilinos e os respetivos utilizadores de forma programática. Seguem-se algumas atividades que pode realizar como administrador:

  • Gestão de utilizadores: crie, atualize, elimine e liste utilizadores para um inquilino específico.

  • Validação de identidade: identifique os utilizadores de uma app para restringir o acesso a recursos no seu próprio servidor.

  • Importar utilizadores: migre utilizadores de um sistema de autenticação externo ou de outro projeto ou inquilino da Identity Platform.

  • Controlo de acesso com reivindicações personalizadas: defina atributos personalizados em contas de utilizador para um inquilino específico e implemente várias estratégias de controlo de acesso, como o controlo de acesso baseado em funções.

  • Gestão de sessões de utilizadores: revogue os tokens de atualização de um utilizador para um inquilino específico.

  • Links de ações por email: gere links de email personalizados para a reposição de palavras-passe, o início de sessão por link de email e a validação de email para utilizadores de um inquilino específico.

  • Gestão de inquilinos: crie, liste, obtenha, atualize e elimine inquilinos para um projeto específico do Identity Platform.

  • Faça a gestão de fornecedores OIDC e SAML em inquilinos: faça a gestão programática de configurações OIDC e SAML num inquilino especificado.

Antes de começar

Funcionalidades suportadas

A tabela seguinte indica as funcionalidades suportadas por cada SDK num ambiente multiinquilino:

Funcionalidade Node.js Java Python Ir C#
Criação de tokens personalizados
Validar tokens de ID
Gerir utilizadores
Controlar o acesso com reivindicações personalizadas
Revogar tokens de atualização
A importar utilizadores
Gerar links de ação de email
Autenticação multifator
Gerir configurações de fornecedores SAML/OIDC
Gestão de cookies de sessão

A tabela seguinte mostra os métodos de início de sessão que pode configurar através do Admin SDK e da Google Cloud consola num contexto específico do inquilino:

Funcionalidade Google Cloud consola SDK de administrador
Email
OIDC
SAML
Redes sociais
Telefone
Autenticação multifator
Anónimo

Gestão de inquilinos

Através do SDK de administrador, pode gerir inquilinos programaticamente a partir de um ambiente de servidor seguro em vez de usar a Google Cloud consola. Isto inclui a capacidade de criar, listar, obter, modificar ou eliminar inquilinos.

Cada inquilino contém os seus próprios fornecedores de identidade, definições e conjuntos de utilizadores. As operações de gestão da configuração do inquilino (CRUD) estão disponíveis a partir da instância do projeto principal através da API admin.auth().tenantManager().

Uma configuração de inquilino fornece informações sobre um inquilino, como o respetivo nome a apresentar, identificador de inquilino e configuração de autenticação de email.

Todas as outras definições (como domínios na lista de autorizações e URIs de redirecionamento autenticados) de um inquilino são herdadas do projeto principal. Têm de ser geridas através da Google Cloud consola.

Para operações como a gestão de utilizadores específica do inquilino, a configuração de fornecedores OIDC/SAML e a geração de links de email, precisa de uma instância TenantAwareAuth para o inquilino de destino (identificado pelo respetivo tenantId exclusivo).

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);

Todas as chamadas para as APIs de gestão de utilizadores, as APIs de gestão de fornecedores OIDC/SAML e as APIs de geração de links de email estarão no âmbito deste inquilino (através da respetiva instância TenantAwareAuth).

Obter um inquilino existente

O SDK de administração fornece o método getTenant(), que obtém informações acerca de um inquilino com base no respetivo tenantId (um identificador exclusivo do inquilino).

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());

Este método devolve um objeto Tenant correspondente ao tenantId. Se o tenantId fornecido não pertencer a um inquilino existente, a promessa devolvida é rejeitada com um erro auth/tenant-not-found.

Tenha cuidado para não confundir uma instância Tenant com um objeto TenantAwareAuth. authInstance.tenantManager().authForTenant() devolve uma instância de TenantAwareAuth que se estende a BaseAuth. A Auth aula também se estende BaseAuth. BaseAuth fornece APIs para gerir utilizadores e configurar fornecedores OIDC/SAML em diferentes contextos. Para Auth, o contexto está ao nível do projeto principal. Para TenantAwareAuth, o contexto está ao nível do inquilino (o inquilino é determinado pelo ID do inquilino). O método getTenant() é resolvido com informações básicas do inquilino (como o ID do inquilino, o nome a apresentar e as definições do fornecedor de email), mas, para chamar APIs nesse inquilino, tem de usar authForTenant(tenantFromGetTenant.tenantId).

Criar um inquilino

Use o método createTenant() para criar uma nova configuração de inquilino:

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());

Pode fornecer qualquer combinação destas propriedades:

Propriedade Tipo Descrição
displayName
string
    
O nome a apresentar do inquilino. Tem de ter entre 4 e 20 carateres, consistir em letras, dígitos e hífenes, e começar por uma letra.
emailSignInConfig
{
  enable: boolean,
  passwordRequired: boolean
}
    
A configuração do fornecedor de início de sessão por email. Isto inclui se o fornecedor de email está ativado e se é necessária uma palavra-passe para iniciar sessão por email. Quando não é obrigatório, o início de sessão por email pode ser feito com uma palavra-passe ou através do início de sessão por link de email.
multiFactorConfig
{
  state: 'DISABLED' | 'ENABLED',
  factorIds: string[]
}
    
Se a autenticação multifator está ativada para o inquilino e que tipos de fatores são permitidos. Atualmente, o único ID do fator suportado é phone.
testPhoneNumbers
{
  string: string
}
  
Um mapa de números de telefone e os respetivos códigos de autenticação multifator associados para registo para fins de teste. São permitidas, no máximo, 10 entradas. Para remover todos os números de telefone de teste, defina este campo como null.

O método devolve um objeto Tenant para o inquilino criado recentemente.

Atualizar um inquilino

Use o método updateTenant() para modificar os dados de um inquilino existente. Tem de especificar o tenantId, juntamente com as propriedades a atualizar para esse inquilino.

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() aceita as mesmas propriedades que createTenant(). Todas as propriedades são opcionais. Se uma propriedade não for especificada, o valor existente não é modificado.

O método devolve um objeto Tenant atualizado após a conclusão. Se o tenantId fornecido não pertencer a um inquilino existente, a promessa devolvida é rejeitada com um erro auth/tenant-not-found.

Eliminar um inquilino

Pode eliminar um inquilino através do respetivo 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);

O método devolve um resultado vazio quando a eliminação é concluída com êxito. Se o tenantId fornecido não pertencer a um inquilino existente, a promessa devolvida é rejeitada com um erro auth/tenant-not-found.

Inquilinos de fichas

Use o método listTenants() para listar os inquilinos existentes:

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());
}

Cada lote de resultados contém uma lista de inquilinos, além de um token de página seguinte para listar o lote seguinte de inquilinos. Quando todos os inquilinos já foram listados, não é devolvido nenhum pageToken.

Se não for especificado nenhum campo maxResults, a predefinição é de 1000 inquilinos por lote. Este é também o número máximo de inquilinos que podem ser apresentados em simultâneo. Qualquer valor superior ao máximo gera um erro de argumento. Se não for especificado nenhum pageToken, o método apresenta uma lista de inquilinos desde o início.

Gerir fornecedores SAML e OIDC de forma programática

O SDK de administrador fornece APIs para gerir programaticamente as configurações do fornecedor de linguagem de marcação de declarações de segurança (SAML) 2.0 e OpenID Connect (OIDC) a partir de um ambiente de servidor seguro.

Com o SDK Admin, pode gerir estes fornecedores para um inquilino específico. Isto é semelhante à gestão de fornecedores OIDC e SAML ao nível do projeto.

Para gerir fornecedores para um inquilino, crie primeiro uma instância 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");

Em seguida, pode realizar operações comuns, como criar, modificar ou eliminar fornecedores para um inquilino.

Criar um fornecedor

O código seguinte mostra como criar um fornecedor SAML para um inquilino:

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());

Modificar um fornecedor

O código seguinte mostra como modificar um fornecedor:

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());

Obter um fornecedor

O código seguinte mostra como obter a configuração do fornecedor para um inquilino específico através do respetivo ID do fornecedor:

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());

Fornecedores de fichas

O código seguinte mostra como listar as configurações de fornecedores para um determinado inquilino:

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());
}

Eliminar um fornecedor

O código seguinte mostra como eliminar um fornecedor:

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");

Os fornecedores OIDC são geridos de forma semelhante aos fornecedores OIDC ao nível do projeto, exceto que podem ser geridos a partir da instância TenantAwareAuth correspondente, em vez de uma instância ao nível do projeto Auth.

Para saber mais, consulte o artigo Gerir fornecedores SAML e OIDC através de programação.

Gerir utilizadores específicos do inquilino

Pode usar o SDK de administrador para criar, obter, atualizar, eliminar e listar todos os utilizadores de um inquilino específico.

Para começar, precisa de uma instância do TenantAwareAuth para o inquilino correspondente:

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");

Obter um utilizador

Pode obter um utilizador específico do inquilino com um identificador 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());

Também pode identificar um utilizador pelo respetivo 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());

A criar um utilizador

Use o método createUser() para criar novos utilizadores para um inquilino específico. Ao criar um novo utilizador, fornecer um uid é opcional. Se não for especificado, a Identity Platform vai aprovisionar um exclusivo.

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());

Modificar um utilizador

Pode modificar os utilizadores existentes especificando o respetivo uid no método 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());

Eliminar um utilizador

O exemplo seguinte mostra como eliminar um utilizador com base no respetivo 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);

Listar utilizadores

Para obter uma lista completa de utilizadores de um inquilino específico em lotes, use o método listUsers(). Cada lote contém uma lista de registos de utilizadores, além de um token de página seguinte se existirem utilizadores adicionais.

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();
}

Consulte a documentação do SDK de administrador sobre como gerir utilizadores para saber mais.

A importar utilizadores

Pode usar o SDK de administrador para importar utilizadores em massa para um inquilino específico com privilégios elevados. Isto oferece várias vantagens, como a capacidade de migrar utilizadores de outro produto da Identity Platform, de outro inquilino ou de um sistema de autenticação externo através de um algoritmo de hash diferente. Também pode importar utilizadores com fornecedores federados (como SAML e OIDC) e reivindicações personalizadas diretamente em massa.

Para começar, obtenha uma instância TenantAwareAuth para o inquilino correspondente:

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");

Pode importar até 1000 utilizadores de cada vez através de um algoritmo de hash específico.

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());
}

O valor de tenantId de todos os utilizadores importados é definido como tenantAuth.tenantId.

Também é possível importar utilizadores sem palavras-passe para um inquilino específico. Estes utilizadores podem ser importados com fornecedores federados e reivindicações personalizadas.

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());
}

Consulte o artigo Importar utilizadores na documentação do SDK de administrador para saber mais.

Validação de identidade

Quando uma app cliente do Identity Platform comunica com um servidor de back-end personalizado, o utilizador com sessão iniciada atual tem de ser identificado nesse servidor. Isto pode ser feito de forma segura através do envio do token de ID do utilizador após o início de sessão bem-sucedido através de uma ligação segura ao seu servidor. Em seguida, o servidor pode validar a integridade e a autenticidade do token de ID.

O SDK de administrador tem um método incorporado para validar e descodificar tokens de ID para um inquilino específico.

Depois de iniciar sessão com êxito de um utilizador num inquilino específico a partir do cliente, obtenha o token de ID do utilizador através do SDK de cliente:

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.
  });

Crie uma instância TenantAwareAuth no servidor:

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");

Em seguida, pode validar o token de ID para esse inquilino específico:

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
  });

Um recurso do lado do servidor pode ser acessível por vários inquilinos com diferentes níveis de acesso. Uma vez que o ID do inquilino pode não ser conhecido antecipadamente neste caso, o token de ID pode ser validado primeiro ao nível do projeto.

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());
}

Consulte a documentação do SDK de administrador sobre como validar tokens de ID para saber mais.

Gerir sessões de utilizadores

As sessões da Identity Platform têm uma longa duração. Sempre que um utilizador inicia sessão, as credenciais do utilizador são validadas no servidor da Identity Platform e, em seguida, trocadas por um token de ID de curta duração e um token de atualização de longa duração. Os tokens de ID duram uma hora. Os tokens de atualização nunca expiram, exceto quando um utilizador é desativado, eliminado ou sofre uma alteração significativa na conta (como uma atualização do email ou da palavra-passe).

Em alguns casos, o token de atualização de um utilizador pode ter de ser revogado por motivos de segurança, como o utilizador denunciar um dispositivo perdido ou roubado, a deteção de uma vulnerabilidade geral numa app ou uma fuga em grande escala de tokens ativos. O SDK de administrador fornece uma API para revogar todos os tokens de atualização emitidos para um utilizador especificado de um inquilino específico.

Para começar, precisa de uma instância do 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");

Em seguida, os tokens de atualização podem ser revogados especificando o uid desse utilizador:

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(f'Tokens revoked at: {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)");

Depois de os tokens de atualização serem revogados, não é possível emitir novos tokens de ID para esse utilizador até que este volte a autenticar-se. No entanto, os tokens de ID existentes permanecem ativos até à respetiva hora de validade natural (uma hora).

Pode verificar se um token de ID válido não expirado não foi revogado especificando o parâmetro checkRevoked opcional. Isto verifica se um token foi revogado depois de a respetiva integridade e autenticidade terem sido validadas.

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
  }
}

Consulte a documentação do SDK de administrador sobre como gerir sessões para saber mais.

Controlar o acesso com reivindicações personalizadas

O SDK de administração suporta a definição de atributos personalizados em contas de utilizador para um inquilino específico. Estes atributos permitem-lhe implementar diferentes estratégias de controlo de acesso, como o controlo de acesso baseado em funções. Os atributos podem ser usados para dar aos utilizadores diferentes níveis de acesso aplicados pelas regras de segurança da aplicação.

Para começar, obtenha uma instância TenantAwareAuth para o inquilino correspondente:

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");

As reivindicações personalizadas podem conter dados confidenciais, pelo que só devem ser definidas a partir de um ambiente de servidor privilegiado através do SDK de administrador.

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.

Os atributos personalizados definidos recentemente aparecem nos atributos de nível superior da carga útil do token na próxima vez que o utilizador iniciar sessão ou atualizar os respetivos tokens de ID numa sessão existente. No exemplo anterior, o token de ID contém uma reivindicação adicional: {admin: true}.

Após validar o token de ID e descodificar a respetiva carga útil, as reivindicações personalizadas adicionais podem ser verificadas para aplicar o controlo de acesso.

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.
}

As reivindicações personalizadas de um utilizador existente para um inquilino específico também estão disponíveis como uma propriedade no registo do utilizador.

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"));

Consulte a documentação do SDK Admin sobre as Reivindicações personalizadas para saber mais.

Com os SDKs de cliente da Identity Platform, pode enviar emails aos utilizadores de um inquilino específico com links que podem usar para repor palavras-passe, validar endereços de email e iniciar sessão com base no email. Estes emails são enviados pela Google e têm opções de personalização limitadas.

Com o SDK Admin, pode gerar estes links de forma programática no âmbito de um inquilino específico.

Para começar, obtenha uma instância TenantAwareAuth para o inquilino correspondente:

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");

O exemplo seguinte mostra como gerar um link para validar o email de um utilizador para um inquilino especificado:

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);

Estão disponíveis mecanismos semelhantes para gerar links de reposição de palavra-passe e início de sessão baseados em email. Tenha em atenção que, quando gera um link de ação de email num contexto de inquilino, o ID do inquilino tem de ser analisado a partir do link e definido na instância do cliente Auth antes de o código poder ser aplicado.

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.
  });

Consulte o artigo Links de ações de email na documentação do SDK Admin para saber mais.

Mensagens de erro

A tabela seguinte apresenta mensagens de erro comuns que pode encontrar.

Código de erro Descrição e passos de resolução
auth/billing-not-enabled Esta funcionalidade requer a ativação da faturação.
auth/invalid-display-name O campo displayName tem de ser uma string válida.
auth/invalid-name O nome do recurso fornecido é inválido.
auth/invalid-page-token O token de página tem de ser uma string não vazia válida.
auth/invalid-project-id Projeto principal inválido. O projeto principal não tem ou não tinha a funcionalidade de multi-inquilino ativada.
auth/invalid-tenant-id O ID do inquilino tem de ser uma string não vazia válida.
auth/mismatching-tenant-id O ID do inquilino do utilizador não corresponde ao ID do inquilino TenantAwareAuth atual.
auth/missing-display-name O recurso que está a ser criado ou editado não tem um nome a apresentar válido.
auth/insufficient-permission O utilizador não tem autorização suficiente para aceder ao recurso pedido ou para executar a operação específica do inquilino.
auth/quota-exceeded A quota do projeto para a operação especificada foi excedida.
auth/tenant-not-found Não existe nenhum inquilino correspondente ao identificador fornecido.
auth/unsupported-tenant-operation Esta operação não é suportada num contexto multiinquilino.
auth/invalid-testing-phone-number Foi fornecido um número de telefone de teste inválido ou um código de teste inválido.
auth/test-phone-number-limit-exceeded Excedeu o número máximo de pares de códigos e números de telefone de teste permitidos.