Como autenticar com multilocação
Este documento mostra como autenticar usuários em um ambiente de vários locatários do Identity Platform.
Antes de começar
Verifique se você ativou a multilocação para seu projeto e configurou os locatários. Consulte Primeiros passos com a multilocação para saber como fazer isso.
Também será necessário adicionar o SDK do cliente ao aplicativo:
Acesse a página do Identity Platform no console do Google Cloud.
Acessar a página de usuários do Identity PlatformNo canto superior direito, clique em Detalhes da configuração da aplicação.
Copie o código no seu app da Web. Por exemplo:
Versão 9 para a Web
import { initializeApp } from "firebase/app"; const firebaseConfig = { apiKey: "...", // By default, authDomain is '[YOUR_APP].firebaseapp.com'. // You may replace it with a custom domain. authDomain: '[YOUR_CUSTOM_DOMAIN]' }; const firebaseApp = initializeApp(firebaseConfig);
Versão 8 para a Web
firebase.initializeApp({ apiKey: '...', // By default, authDomain is '[YOUR_APP].firebaseapp.com'. // You may replace it with a custom domain. authDomain: '[YOUR_CUSTOM_DOMAIN]' });
Fazer login com locatários
Para fazer login em um locatário, o ID dele precisa ser transmitido para o objeto auth
.
Observe que tenantId
não é mantido em recarregamentos de páginas.
Versão 9 para a Web
import { getAuth } from "firebase/auth"; const auth = getAuth(); const tenantId = "TENANT_ID1"; auth.tenantId = tenantId;
Versão 8 para a Web
const tenantId = "TENANT_ID1"; firebase.auth().tenantId = tenantId;
As próximas solicitações de login dessa instância auth
incluirão o ID
de locatário (TENANT_ID1
no exemplo anterior) até que você altere ou redefina o ID
de locatário.
É possível trabalhar com vários locatários usando uma ou várias instâncias auth
.
Para usar uma única instância auth
, modifique a propriedade tenantId
sempre que
quiser alternar entre locatários. Para voltar aos IdPs no nível do projeto, defina
tenantId
como null
:
Versão 9 para a Web
// One Auth instance // Switch to tenant1 auth.tenantId = "TENANT_ID1"; // Switch to tenant2 auth.tenantId = "TENANT_ID2"; // Switch back to project level IdPs auth.tenantId = null;
Versão 8 para a Web
// One Auth instance // Switch to tenant1 firebase.auth().tenantId = "TENANT_ID1"; // Switch to tenant2 firebase.auth().tenantId = "TENANT_ID2"; // Switch back to project level IdPs firebase.auth().tenantId = null;
Para usar várias instâncias, crie uma nova instância auth
para cada locatário e
atribua IDs diferentes a eles:
Versão 9 para a Web
// Multiple Auth instances import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; const firebaseApp1 = initializeApp(firebaseConfig1, 'app1_for_tenantId1'); const firebaseApp2 = initializeApp(firebaseConfig2, 'app2_for_tenantId2'); const auth1 = getAuth(firebaseApp1); const auth2 = getAuth(firebaseApp2); auth1.tenantId = "TENANT_ID1"; auth2.tenantId = "TENANT_ID2";
Versão 8 para a Web
// Multiple Auth instances firebase.initializeApp(config, 'app1_for_tenantId1'); firebase.initializeApp(config, 'app2_for_tenantId2'); const auth1 = firebase.app('app1').auth(); const auth2 = firebase.app('app2').auth(); auth1.tenantId = "TENANT_ID1"; auth2.tenantId = "TENANT_ID2";
Depois de fazer login com um locatário, um usuário de locatário será retornado com
user.tenantId
definido para esse locatário. Se você alterar tenantId
na instância
auth
posteriormente, a propriedade currentUser
não será alterada.
Ela ainda apontará para o mesmo usuário que o locatário anterior.
Versão 9 para a Web
import { signInWithEmailAndPassword, onAuthStateChanged } from "firebase/auth"; // Switch to TENANT_ID1 auth.tenantId = 'TENANT_ID1'; // Sign in with tenant signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { // User is signed in. const user = userCredential.user; // user.tenantId is set to 'TENANT_ID1'. // Switch to 'TENANT_ID2'. auth.tenantId = 'TENANT_ID2'; // auth.currentUser still points to the user. // auth.currentUser.tenantId is 'TENANT_ID1'. }); // You could also get the current user from Auth state observer. onAuthStateChanged(auth, (user) => { if (user) { // User is signed in. // user.tenantId is set to 'TENANT_ID1'. } else { // No user is signed in. } });
Versão 8 para a Web
// Switch to TENANT_ID1 firebase.auth().tenantId = 'TENANT_ID1'; // Sign in with tenant firebase.auth().signInWithEmailAndPassword(email, password) .then((result) => { const user = result.user; // user.tenantId is set to 'TENANT_ID1'. // Switch to 'TENANT_ID2'. firebase.auth().tenantId = 'TENANT_ID2'; // firebase.auth().currentUser still point to the user. // firebase.auth().currentUser.tenantId is 'TENANT_ID1'. }); // You could also get the current user from Auth state observer. firebase.auth().onAuthStateChanged((user) => { if (user) { // User is signed in. // user.tenantId is set to 'TENANT_ID1'. } else { // No user is signed in. } });
Contas de e-mail/senha
O exemplo a seguir mostra como registrar um novo usuário:
Versão 9 para a Web
import { createUserWithEmailAndPassword } from "firebase/auth"; auth.tenantId = 'TENANT_ID'; createUserWithEmailAndPassword(auth, email, password) .then((userCredential) => { // User is signed in. // userCredential.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle / display error. // ... });
Versão 8 para a Web
firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().createUserWithEmailAndPassword(email, password) .then((result) => { // result.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle error. });
Para fazer login com um usuário atual:
Versão 9 para a Web
import { signInWithEmailAndPassword } from "firebase/auth"; auth.tenantId = 'TENANT_ID'; signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { // User is signed in. // userCredential.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle / display error. // ... });
Versão 8 para a Web
firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().signInWithEmailAndPassword(email, password) .then((result) => { // result.user.tenantId is 'TENANT_ID'. }).catch((error) => { // Handle error. });
SAML
Para fazer login com um provedor SAML, instancie uma instância do SAMLAuthProvider
pelo ID do provedor do console do Google Cloud:
Versão 9 para a Web
import { SAMLAuthProvider } from "firebase/auth"; const provider = new SAMLAuthProvider("saml.myProvider");
Versão 8 para a Web
const provider = new firebase.auth.SAMLAuthProvider('saml.myProvider');
Você pode usar um pop-up ou um fluxo de redirecionamento para fazer login no provedor SAML.
Pop-up
Versão 9 para a Web
import { signInWithPopup } from "firebase/auth"; // Switch to TENANT_ID1. auth.tenantId = 'TENANT_ID1'; // Sign-in with popup. signInWithPopup(auth, provider) .then((userCredential) => { // User is signed in. const user = userCredential.user; // user.tenantId is set to 'TENANT_ID1'. // Provider data available from the result.user.getIdToken() // or from result.user.providerData }) .catch((error) => { // Handle / display error. // ... });
Versão 8 para a Web
// Switch to TENANT_ID1. firebase.auth().tenantId = 'TENANT_ID1'; // Sign-in with popup. firebase.auth().signInWithPopup(provider) .then((result) => { // User is signed in. // tenant ID is available in result.user.tenantId. // Identity provider data is available in result.additionalUserInfo.profile. }) .catch((error) => { // Handle error. });
Redirecionamento
Versão 9 para a Web
import { signInWithRedirect, getRedirectResult } from "firebase/auth"; // Switch to TENANT_ID1. auth.tenantId = 'TENANT_ID1'; // Sign-in with redirect. signInWithRedirect(auth, provider); // After the user completes sign-in and returns to the app, you can get // the sign-in result by calling getRedirectResult. However, if they sign out // and sign in again with an IdP, no tenant is used. getRedirectResult(auth) .then((result) => { // User is signed in. // The tenant ID available in result.user.tenantId. // Provider data available from the result.user.getIdToken() // or from result.user.providerData }) .catch((error) => { // Handle / display error. // ... });
Versão 8 para a Web
// Switch to TENANT_ID1. firebase.auth().tenantId = 'TENANT_ID1'; // Sign-in with redirect. firebase.auth().signInWithRedirect(provider); // After the user completes sign-in and returns to the app, you can get // the sign-in result by calling getRedirectResult. However, if they sign out // and sign in again with an IdP, no tenant is used. firebase.auth().getRedirectResult() .then((result) => { // User is signed in. // The tenant ID available in result.user.tenantId. // Identity provider data is available in result.additionalUserInfo.profile. }) .catch((error) => { // Handle error. });
Nos dois casos, defina o ID de locatário correto na instância auth
.
Enviar link por e-mail
Para iniciar o fluxo de autenticação, exiba uma interface solicitando que o usuário
forneça um endereço de e-mail e, em seguida, chame sendSignInLinkToEmail
para enviar
um link de autenticação. Defina o ID do locatário correto na instância auth
antes de enviar o e-mail.
Versão 9 para a Web
import { sendSignInLinkToEmail } from "firebase/auth"; // Switch to TENANT_ID1 auth.tenantId = 'TENANT_ID1'; sendSignInLinkToEmail(auth, email, actionCodeSettings) .then(() => { // The link was successfully sent. Inform the user. // Save the email locally so you don't need to ask the user for it again // if they open the link on the same device. window.localStorage.setItem('emailForSignIn', email); }) .catch((error) => { // Handle / display error. // ... });
Versão 8 para a Web
// Switch to TENANT_ID1 firebase.auth().tenantId = 'TENANT_ID1'; firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings) .then(() => { // The link was successfully sent. Inform the user. // Save the email locally so you don't need to ask the user for it again // if they open the link on the same device. window.localStorage.setItem('emailForSignIn', email); }) .catch((error) => { // Some error occurred, you can inspect the code: error.code });
Para concluir o login na página de destino, primeiro analise o ID do locatário no
link de e-mail e configure-o na instância auth
. Em seguida, chame signInWithEmailLink
com o e-mail do usuário e o link real que contém o código de uso único.
Versão 9 para a Web
import { isSignInWithEmailLink, parseActionCodeURL, signInWithEmailLink } from "firebase/auth"; if (isSignInWithEmailLink(auth, window.location.href)) { const actionCodeUrl = parseActionCodeURL(window.location.href); if (actionCodeUrl.tenantId) { auth.tenantId = actionCodeUrl.tenantId; } let email = window.localStorage.getItem('emailForSignIn'); if (!email) { // User opened the link on a different device. To prevent session fixation // attacks, ask the user to provide the associated email again. For example: email = window.prompt('Please provide your email for confirmation'); } // The client SDK will parse the code from the link for you. signInWithEmailLink(auth, email, window.location.href) .then((result) => { // User is signed in. // tenant ID available in result.user.tenantId. // Clear email from storage. window.localStorage.removeItem('emailForSignIn'); }); }
Versão 8 para a Web
if (firebase.auth().isSignInWithEmailLink(window.location.href)) { const actionCodeUrl = firebase.auth.ActionCodeURL.parseLink(window.location.href); if (actionCodeUrl.tenantId) { firebase.auth().tenantId = actionCodeUrl.tenantId; } let email = window.localStorage.getItem('emailForSignIn'); if (!email) { // User opened the link on a different device. To prevent session fixation // attacks, ask the user to provide the associated email again. For example: email = window.prompt('Please provide your email for confirmation'); } firebase.auth().signInWithEmailLink(email, window.location.href) .then((result) => { // User is signed in. // tenant ID available in result.user.tenantId. }); }
Como criar tokens personalizados
A criação de um token personalizado com reconhecimento de vários locatários é idêntica à criação de um token personalizado regular.
Contanto que o ID de locatário correto tenha sido definido na instância auth
,
uma declaração tenant_id
de nível superior será adicionada ao JWT resultante.
Consulte Como criar tokens personalizados
para instruções detalhadas sobre como criar e usar tokens personalizados.
No exemplo a seguir, mostramos como criar um token personalizado usando o SDK Admin:
Versão 9 para a Web
// Ensure you're using a tenant-aware auth instance const tenantManager = admin.auth().tenantManager(); const tenantAuth = tenantManager.authForTenant('TENANT_ID1'); // Create a custom token in the usual manner tenantAuth.createCustomToken(uid) .then((customToken) => { // Send token back to client }) .catch((error) => { console.log('Error creating custom token:', error); });
Versão 8 para a Web
// Ensure you're using a tenant-aware auth instance const tenantManager = admin.auth().tenantManager(); const tenantAuth = tenantManager.authForTenant('TENANT_ID1'); // Create a custom token in the usual manner tenantAuth.createCustomToken(uid) .then((customToken) => { // Send token back to client }) .catch((error) => { console.log('Error creating custom token:', error); });
O código a seguir demonstra como fazer login usando um token personalizado:
Versão 9 para a Web
import { signInWithCustomToken } from "firebase/auth"; auth.tenantId = 'TENANT_ID1'; signInWithCustomToken(auth, token) .catch((error) => { // Handle / display error. // ... });
Versão 8 para a Web
firebase.auth().tenantId = 'TENANT_ID1'; firebase.auth().signInWithCustomToken(token) .catch((error) => { // Handle Errors here. const errorCode = error.code; const errorMessage = error.message; // ... });
Observe que, se os IDs de locatário não corresponderem, o método signInWithCustomToken()
falhará.
Como vincular credenciais de usuário de vários locatários
Você pode vincular outros tipos de credenciais a um usuário de vários locatários atual. Por exemplo, se um usuário foi autenticado anteriormente com um provedor SAML em um locatário, você pode adicionar o login por e-mail/senha à conta atual para que ele possa usar o método de login no locatário.
Versão 9 para a Web
import { signInWithPopup, EmailAuthProvider, linkWithCredential, SAMLAuthProvider, signInWithCredential } from "firebase/auth"; // Switch to TENANT_ID1 auth.tenantId = 'TENANT_ID1'; // Sign-in with popup signInWithPopup(auth, provider) .then((userCredential) => { // Existing user with e.g. SAML provider. const prevUser = userCredential.user; const emailCredential = EmailAuthProvider.credential(email, password); return linkWithCredential(prevUser, emailCredential) .then((linkResult) => { // Sign in with the newly linked credential const linkCredential = SAMLAuthProvider.credentialFromResult(linkResult); return signInWithCredential(auth, linkCredential); }) .then((signInResult) => { // Handle sign in of merged user // ... }); }) .catch((error) => { // Handle / display error. // ... });
Versão 8 para a Web
// Switch to TENANT_ID1 firebase.auth().tenantId = 'TENANT_ID1'; // Sign-in with popup firebase.auth().signInWithPopup(provider) .then((result) => { // Existing user with e.g. SAML provider. const user = result.user; const emailCredential = firebase.auth.EmailAuthProvider.credential(email, password); return user.linkWithCredential(emailCredential); }) .then((linkResult) => { // The user can sign in with both SAML and email/password now. });
Ao vincular ou autenticar novamente um usuário de vários locatários atual,
auth.tenantId
será ignorado. use user.tenantId
para especificar qual locatário
usar. Isso também se aplica a outras APIs de gerenciamento de usuários, como updateProfile
e updatePassword
.
Como processar os erros account-exists-with-different-credential
Se você ativou a configuração Vincular contas que usam o mesmo e-mail em
Console do Google Cloud, quando um usuário tenta fazer login em um provedor
(como SAML) com um e-mail que já existe para outro provedor (como
Google), o erro auth/account-exists-with-different-credential
será gerado
(com um objeto AuthCredential
).
Para concluir o login com o provedor pretendido, o usuário precisará fazer login
no provedor atual (Google) e, em seguida, vinculá-lo ao AuthCredential
anterior (SAML).
Você pode usar um fluxo pop-up ou de redirecionamento para lidar com esse erro.
Pop-up
Versão 9 para a Web
import { signInWithPopup, fetchSignInMethodsForEmail, linkWithCredential } from "firebase/auth"; // Step 1. // User tries to sign in to the SAML provider in that tenant. auth.tenantId = 'TENANT_ID'; signInWithPopup(auth, samlProvider) .catch((error) => { // An error happened. if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. // The pending SAML credential. const pendingCred = error.credential; // The credential's tenantId if needed: error.tenantId // The provider account's email address. const email = error.customData.email; // Get sign-in methods for this email. fetchSignInMethodsForEmail(email, auth) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { signInWithPopup(auth, googleProvider) .then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. linkWithCredential(result.user, pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); }); } }); } });
Versão 8 para a Web
// Step 1. // User tries to sign in to the SAML provider in that tenant. firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().signInWithPopup(samlProvider) .catch((error) => { // An error happened. if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. // The pending SAML credential. const pendingCred = error.credential; // The credential's tenantId if needed: error.tenantId // The provider account's email address. const email = error.email; // Get sign-in methods for this email. firebase.auth().fetchSignInMethodsForEmail(email) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { firebase.auth().signInWithPopup(googleProvider) .then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. result.user.linkWithCredential(pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); }); } }); } });
Redirecionamento
Ao usar
signInWithRedirect
, os errosauth/account-exists-with-different-credential
serão gerados emgetRedirectResult
ao finalizar o fluxo de redirecionamento.O objeto de erro contém a propriedade
error.tenantId
. Como o ID de locatário na instânciaauth
não é mantido após o redirecionamento, você precisa definir o ID de locatário do objeto de erro para a instânciaauth
.O exemplo a seguir mostra como lidar com o erro:
Versão 9 para a Web
import { signInWithRedirect, getRedirectResult, fetchSignInMethodsForEmail, linkWithCredential } from "firebase/auth"; // Step 1. // User tries to sign in to SAML provider. auth.tenantId = 'TENANT_ID'; signInWithRedirect(auth, samlProvider); var pendingCred; // Redirect back from SAML IDP. auth.tenantId is null after redirecting. getRedirectResult(auth).catch((error) => { if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. const tenantId = error.tenantId; // The pending SAML credential. pendingCred = error.credential; // The provider account's email address. const email = error.customData.email; // Need to set the tenant ID again as the page was reloaded and the // previous setting was reset. auth.tenantId = tenantId; // Get sign-in methods for this email. fetchSignInMethodsForEmail(auth, email) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { signInWithRedirect(auth, googleProvider); } }); } }); // Redirect back from Google. auth.tenantId is null after redirecting. getRedirectResult(auth).then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. // result.user.tenantId is 'TENANT_ID'. linkWithCredential(result.user, pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); });
Versão 8 para a Web
// Step 1. // User tries to sign in to SAML provider. firebase.auth().tenantId = 'TENANT_ID'; firebase.auth().signInWithRedirect(samlProvider); var pendingCred; // Redirect back from SAML IDP. auth.tenantId is null after redirecting. firebase.auth().getRedirectResult().catch((error) => { if (error.code === 'auth/account-exists-with-different-credential') { // Step 2. // User's email already exists. const tenantId = error.tenantId; // The pending SAML credential. pendingCred = error.credential; // The provider account's email address. const email = error.email; // Need to set the tenant ID again as the page was reloaded and the // previous setting was reset. firebase.auth().tenantId = tenantId; // Get sign-in methods for this email. firebase.auth().fetchSignInMethodsForEmail(email) .then((methods) => { // Step 3. // Ask the user to sign in with existing Google account. if (methods[0] == 'google.com') { firebase.auth().signInWithRedirect(googleProvider); } }); } }); // Redirect back from Google. auth.tenantId is null after redirecting. firebase.auth().getRedirectResult().then((result) => { // Step 4 // Link the SAML AuthCredential to the existing user. // result.user.tenantId is 'TENANT_ID'. result.user.linkWithCredential(pendingCred) .then((linkResult) => { // SAML account successfully linked to the existing // user. goToApp(); }); });
Desativação da criação e exclusão de contas de usuários finais
Há situações em que você pode querer que os administradores criem contas de usuário, e não contas criadas por ações do usuário. Nesses casos, você pode desativar as ações do usuário em nossa API REST:
curl --location --request PATCH 'https://identitytoolkit.googleapis.com/v2/projects/PROJECT_ID/tenants/TENANT_ID?updateMask=client' \ --header 'Authorization: Bearer AUTH_TOKEN' \ --header 'Content-Type: application/json' \ --data-raw '{ "client": { "permissions": { "disabled_user_signup": true, "disabled_user_deletion": true } } }'
Substitua:
AUTH_TOKEN
: o token de autenticação.PROJECT_ID
: o ID do projeto;TENANT_ID
: o ID do locatário.
A seguir
- Criar uma página de login para vários locatários
- Migrar usuários atuais para um locatário
- Gerenciar locatários de forma programática