Como gerenciar locatários do Identity Platform de maneira programática

Este documento explica como usar o SDK Admin do Identity Platform para gerenciar locatários e usuários de maneira programática. Algumas atividades que podem ser executadas como administrador incluem:

  • Gerenciamento de usuários: crie, atualize, exclua e liste usuários de um locatário específico.

  • Verificação de identidade: identifique os usuários de um app para restringir o acesso aos recursos no seu servidor.

  • Importar usuários: migre usuários de um sistema de autenticação externo ou de outro projeto ou locatário do Identity Platform.

  • Controle de acesso com declarações personalizadas: defina atributos personalizados em contas de usuário para um locatário específico e implemente várias estratégias de controle de acesso, como controle de acesso baseado em papel.

  • Gerenciamento da sessão do usuário: revogue os tokens de atualização de um usuário para um locatário específico.

  • Links de ação de e-mail: gere links de e-mail personalizados para redefinição de senha, login por link de e-mail e verificação de e-mail para usuários de um locatário específico.

  • Gerenciamento de locatários: crie, liste, receba, atualize e exclua locatários para um projeto específico do Identity Platform.

  • Gerenciar provedores de OIDC e SAML em locatários: gerencie de maneira programática as configurações de OIDC e SAML em um locatário especificado.

Antes de começar

Recursos compatíveis

A tabela a seguir lista os recursos compatíveis com cada SDK em um ambiente de vários locatários:

Feature Node.js Java Python Go C#
Criação de tokens personalizados
Como verificar tokens de ID
Gerenciamento de usuários
Como controlar o acesso com declarações personalizadas
Como revogar tokens de atualização
Como importar usuários
Como gerar links de ação de e-mail
Autenticação multifator
Como gerenciar configurações do provedor SAML/OIDC
Gerenciamento de cookies de sessão

A tabela a seguir mostra os métodos de login que podem ser configurados usando o SDK Admin e o console do Google Cloud em um contexto específico do locatário:

Engenharia de Console do Google Cloud SDK Admin
E-mail
OIDC
SAML
Sociais
Telefone
Autenticação multifator
Anônimo

Gestão de inquilinos

Com o SDK Admin, é possível gerenciar locatários de maneira programática em um ambiente de servidor seguro em vez de usar o console do Google Cloud. Isso inclui a capacidade de criar, listar, conseguir, modificar ou excluir locatários.

Cada locatário contém seus próprios provedores de identidade, configurações e conjuntos de usuários. Operações de gerenciamento de configuração (CRUD) estão disponíveis na instância do projeto pai usando o admin.auth().tenantManager().

Uma configuração de locatário fornece informações sobre um locatário, como nome de exibição, identificador de locatário e configuração de autenticação de e-mail.

Todas as outras configurações (como domínios permitidos e URIs de redirecionamento autenticados) de um locatário são herdadas do projeto pai. Eles precisam ser gerenciados usando o console do Google Cloud.

Para operações como gerenciamento de usuários específico do locatário, configuração de provedores OIDC/SAML e geração de links de e-mail, você precisará de uma instância TenantAwareAuth para o locatário de destino (identificado pelo 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 gerenciamento de usuários, APIs de gerenciamento de provedor OIDC/SAML e APIs de geração de link de e-mail estarão dentro do escopo desse locatário (usando a instância TenantAwareAuth).

Como conseguir um locatário atual

O SDK Admin fornece o método getTenant(), que busca informações sobre um locatário com base em seu tenantId (um identificador exclusivo para o locatário).

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

Esse método retorna um objeto Tenant correspondente ao tenantId. Se o tenantId fornecido não pertencer a um locatário atual, a promessa retornada será 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() retorna uma instância TenantAwareAuth que estende BaseAuth. A classe Auth também estende BaseAuth. BaseAuth fornece APIs para gerenciar usuários e configura provedores OIDC/SAML em contextos diferentes. Para Auth, o contexto está no nível do projeto pai. Para TenantAwareAuth, o contexto está no nível de locatário (o locatário é determinado pelo ID do locatário). O getTenant() do método será resolvido com informações básicas sobre o locatário (como ID de locatário, nome de exibição e configurações do provedor de e-mail), mas para chamar APIs nesse locatário, você precisa usar authForTenant(tenantFromGetTenant.tenantId).

Como criar um locatário

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

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

É possível fornecer qualquer combinação dessas propriedades:

Property Tipo Descrição
displayName

string
    
O nome de exibição do locatário. Precisa ter de 4 a 20 caracteres, contendo letras, dígitos e hífens e começar com uma letra.
emailSignInConfig

{
  enable: boolean,
  passwordRequired: boolean
}
    
Configuração do provedor de login de e-mail. Isso inclui se o provedor de e-mail está ativado e se a senha é necessária para o login por e-mail. Quando não é obrigatório, o login de e-mail pode ser realizado com senha ou usando o login por link de e-mail.
multiFactorConfig

{
  state: 'DISABLED' | 'ENABLED',
  factorIds: string[]
}
    
Se a autenticação multifator está ativada para o locatário ou não, e quais tipos de fator são permitidos. Atualmente, o único código de fator compatível é phone.
testPhoneNumbers

{
  string: string
}
  
Um mapa de números de telefone e os códigos de autenticação multifator associados a fim de se registrar 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 retorna um objeto Tenant para o locatário recém-criado.

Como atualizar um locatário

Use o método updateTenant() para modificar os dados de um locatário atual. É necessário especificar o tenantId e as propriedades a serem atualizadas para o locatário em questão.

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 atual não será modificado.

O método retorna um objeto Tenant atualizado após a conclusão. Se o tenantId fornecido não pertencer a um locatário atual, a promessa retornada será rejeitada com um erro auth/tenant-not-found.

Como excluir um locatário

É possível excluir um locatário usando o 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 retorna um resultado vazio quando a exclusão é concluída com sucesso. Se o tenantId fornecido não pertencer a um locatário atual, a promessa retornada será rejeitada com um erro auth/tenant-not-found.

Como listar locatários

Use o método listTenants() para listar os locatários atuais:

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 locatários, além de um token de próxima página para listar o próximo lote de locatário. Quando todos os locatários já tiverem sido listados, nenhum pageToken será retornado.

Se nenhum campo maxResults for especificado, o padrão será 1.000 locatários por lote. Esse também é o número máximo de locatários permitidos por vez. Qualquer valor maior que o máximo lançará um erro de argumento. Se nenhum pageToken for especificado, o método lista os locatários desde o início.

Como gerenciar provedores SAML e OIDC de forma programática

O SDK Admin fornece APIs para gerenciar as configurações do provedor da Linguagem de marcação para autorização de segurança (SAML, na sigla em inglês) 2.0 e do OpenID Connect (OIDC) de maneira programática em um ambiente de servidor seguro.

Com o SDK Admin, você pode gerenciar esses provedores para um locatário específico. Isso é semelhante ao gerenciamento de provedores OIDC e SAML no nível do projeto.

Para gerenciar provedores de um locatário, primeiro crie 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, você pode realizar operações comuns, como criar, modificar ou excluir provedores de um locatário.

Como criar um provedor

O código a seguir mostra como criar um provedor SAML para um locatário:

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

Como modificar um provedor

O código a seguir mostra como modificar um provedor:

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

Como conseguir um provedor

O código a seguir mostra como recuperar a configuração do provedor para um locatário específico usando o ID do provedor:

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

Como listar provedores

O código a seguir mostra como listar configurações de provedor de um determinado locatário:

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

Como excluir um provedor

O código a seguir mostra como excluir um provedor:

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 provedores de OIDC são gerenciados de maneira semelhante aos provedores de OIDC no nível do projeto, mas podem ser gerenciados na instância TenantAwareAuth correspondente, em vez de uma instância no nível do projeto Auth.

Para saber mais, consulte Como gerenciar provedores SAML e OIDC de maneira programática.

Como gerenciar usuários específicos de locatário

É possível usar o SDK Admin para criar, recuperar, atualizar, excluir e listar todos os usuários de um locatário específico.

Para começar, você precisa de uma instância de TenantAwareAuth para o locatário 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");

Como receber um usuário

É possível recuperar um usuário específico de locatário 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());

Você também pode identificar um usuário por e-mail:

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

Criar um usuário

Use o método createUser() para criar novos usuários para um locatário específico. Ao criar um novo usuário, fornecer um uid é opcional. Se não for especificado, o Identity Platform provisionará um único.

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

Como modificar um usuário

É possível modificar os usuários atuais especificando uid deles para o 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());

Como excluir um usuário

O exemplo a seguir mostra como excluir um usuário com base no 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 usuários

Para recuperar uma lista inteira de usuários de um locatário específico em lotes, use o método listUsers(). Cada lote conterá uma lista de registros de usuário e um token de próxima página, caso outros usuários permaneçam.

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 Admin sobre como gerenciar usuários para saber mais.

Como importar usuários

É possível usar o SDK Admin para importar usuários em massa para um locatário específico com privilégios elevados. Isso oferece vários benefícios, como a capacidade de migrar usuários de outro produto do Identity Platform, de outro locatário ou de um sistema de autenticação externo usando um algoritmo de hash diferente. Também é possível importar usuários com provedores federados (como SAML e OIDC) e declarações personalizadas diretamente em massa.

Para começar, consiga uma instância TenantAwareAuth para o locatário 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");

Você pode importar até mil usuários de uma só vez usando 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());
}

Todos os usuários importados terão o tenantId definido como tenantAuth.tenantId.

Os usuários sem senhas também podem ser importados para um locatário específico. Esses usuários podem ser importados com provedores federados e declaraçõ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 Importar usuários na documentação do SDK Admin para saber mais.

Verificação da identidade

Quando um aplicativo cliente do Identity Platform se comunica com um servidor de back-end personalizado, o usuário conectado atual precisa ser identificado nesse servidor. Isso pode ser feito com segurança enviando o token de ID do usuário após o login bem-sucedido usando uma conexão segura com seu servidor. O servidor pode verificar a integridade e a autenticidade do token de ID.

O SDK Admin tem um método integrado para verificar e decodificar tokens de ID para um locatário específico.

Depois de fazer login com sucesso em um usuário a um locatário específico do cliente, recupere o token de ID do usuário usando o SDK do 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");

Assim, você pode verificar o token de ID para esse locatário 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 acessado por vários locatários com diferentes níveis de acesso. Como o ID do locatário pode não ser conhecido com antecedência nesse caso, o token de ID pode ser verificado primeiro no 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());
}

Veja mais informações no documento sobre como verificar os tokens de ID do SDK Admin.

Como gerenciar sessões de usuários

As sessões do Identity Platform têm longa duração. Sempre que um usuário faz login, as credenciais do usuário são verificadas no servidor do Identity Platform e 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 por uma hora. Os tokens de atualização nunca expiram, exceto quando um usuário é desativado, excluído ou passa por uma grande alteração de conta (como uma atualização de e-mail ou senha).

Em alguns casos, o token de atualização de um usuário pode precisar ser revogado por motivos de segurança, como se o usuário relata um dispositivo perdido ou roubado, a descoberta de uma vulnerabilidade geral em um app ou um vazamento em grande escala de tokens ativos. O SDK Admin fornece uma API para revogar todos os tokens de atualização emitidos para um usuário especificado de um locatário específico.

Para começar, você precisa de 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");

Os tokens de atualização podem ser revogados especificando o uid desse usuário:

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

Depois que os tokens de atualização forem revogados, nenhum novo token de ID poderá ser emitido para esse usuário até que ele seja autenticado novamente. No entanto, os tokens de ID atuais permanecerão ativos até o tempo de expiração natural (uma hora).

É possível verificar se um token de ID válido não expirado não é revogado especificando o parâmetro checkRevoked opcional. Ele verifica se um token é revogado depois que a integridade e a autenticidade dele são verificadas.

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

Veja a documentação do SDK Admin sobre como gerenciar sessões para saber mais.

Como controlar o acesso com declarações personalizadas

O SDK Admin é compatível com a definição de atributos personalizados em contas de usuário para um locatário específico. Esses atributos permitem implementar diferentes estratégias de controle de acesso, como o controle de acesso baseado em papéis. Os atributos podem ser usados para oferecer aos usuários diferentes níveis de acesso aplicados pelas regras de segurança do aplicativo.

Para começar, consiga uma instância de TenantAwareAuth para o locatário 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 declarações personalizadas podem conter dados confidenciais. Portanto, elas só podem ser definidas em um ambiente de servidor privilegiado usando o 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.

Os atributos personalizados recém-definidos aparecerão nos atributos de nível superior do payload do token na próxima vez que o usuário fizer login ou atualizar os tokens de ID em uma sessão atual. No exemplo anterior, o token de ID contém uma declaração extra: {admin: true}.

Depois de verificar o token de ID e decodificar o payload, as declarações personalizadas adicionais podem ser verificadas para impor o controle 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 declarações personalizadas para um usuário atual de um locatário específico também estão disponíveis como uma propriedade no registro do usuário.

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 declarações personalizadas para saber mais.

Com os SDKs de cliente do Identity Platform, você pode enviar usuários de e-mails de locatários específicos com links que eles podem usar para redefinições de senha, verificação de endereço de e-mail e login com base em e-mail. Esses e-mails são enviados pelo Google e têm personalização limitada.

Com o SDK Admin, é possível gerar esses links de maneira programática no escopo de um locatário específico.

Para começar, consiga uma instância de TenantAwareAuth para o locatário 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 a seguir mostra como gerar um link para verificar o e-mail de um usuário para um locatário 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);

Mecanismos semelhantes estão disponíveis para gerar redefinição de senha e links de login com base em e-mail. Ao gerar um link de ação de e-mail em um contexto de locatário, o ID do locatário precisa ser analisado a partir do link e definido na instância Auth do cliente antes que o código possa 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 Links de ação por e-mail na documentação do SDK Admin para saber mais.

Mensagens de erro

A tabela a seguir lista as mensagens de erro comuns que você pode encontrar.

Código do erro Etapas de resolução e descrição
auth/billing-not-enabled Esse recurso exige que o faturamento esteja ativado.
auth/invalid-display-name O campo displayName precisa ser uma string válida.
auth/invalid-name O nome do recurso fornecido é inválido.
auth/invalid-page-token O token de página precisa ser uma string não vazia válida.
auth/invalid-project-id Projeto pai inválido. O projeto pai não ativa ou não ativou a multilocação.
auth/invalid-tenant-id O ID do locatário precisa ser uma string não vazia válida.
auth/mismatching-tenant-id O ID de locatário do usuário não corresponde ao ID de locatário atual do TenantAwareAuth.
auth/missing-display-name O recurso que está sendo criado ou editado não tem um nome de exibição válido.
auth/insufficient-permission O usuário não tem permissão suficiente para acessar o recurso solicitado ou executar a operação de locatário específica.
auth/quota-exceeded A cota do projeto para a operação especificada foi excedida.
auth/tenant-not-found Não há locatário correspondente ao identificador fornecido.
auth/unsupported-tenant-operation Essa operação não é compatível com um contexto de vários locatários.
auth/invalid-testing-phone-number Um número de telefone de teste inválido ou um código de teste inválido foi fornecido.
auth/test-phone-number-limit-exceeded O número máximo permitido de pares de número de telefone e código de teste foi excedido.