Neste artigo, você vai aprender a criar sua própria página de autenticação usando identidades externas e o IAP. Criar esta página por conta própria oferece controle total sobre o fluxo de autenticação e a experiência do usuário.
Se você não precisa personalizar totalmente sua IU, permita que o IAP hospede uma página de login para você ou use o FirebaseUI para uma experiência mais simplificada.
Visão geral
Para criar sua própria página de autenticação, siga estas etapas:
- Ative identidades externas. Selecione Vou oferecer minha própria UI interface durante a configuração.
- Instale a biblioteca
gcip-iap
. - Configure a UI implementando a interface
AuthenticationHandler
. Sua página de autenticação precisa processar os seguintes cenários:- Seleção de locatário
- Autorização do usuário
- Login do usuário
- Tratamento de erros
- Opcional: personalize sua página de autenticação com outros recursos, como barras de progresso, páginas de desativação e processamento do usuário.
- Teste a interface.
Como instalar a biblioteca gcip-iap
Para instalar a biblioteca gcip-iap
, execute o seguinte comando:
npm install gcip-iap --save
O módulo gcip-iap
do NPM abstrai as comunicações entre seu aplicativo, o IAP e o Identity Platform. Isso permite que você personalize
fluxo de autenticação sem precisar gerenciar as trocas subjacentes entre o
UI e IAP.
Use as importações corretas para sua versão do SDK:
gcip-iap v0.1.4 ou anterior
// Import Firebase/GCIP dependencies. These are installed on npm install.
import * as firebase from 'firebase/app';
import 'firebase/auth';
// Import GCIP/IAP module.
import * as ciap from 'gcip-iap';
gcip-iap v1.0.0 a v1.1.0
A partir da versão v1.0.0, o gcip-iap
requer a dependência de peer firebase
v9 ou mais recente.
Se você estiver migrando para a gcip-iap
v1.0.0 ou mais recente, conclua as seguintes
ações:
- Atualize a versão do
firebase
no arquivopackage.json
para v9.6.0 ou mais recente. - Atualize as instruções de importação
firebase
desta forma:
// Import Firebase modules.
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
Nenhuma outra alteração de código é necessária.
gcip-iap v2.0.0
A partir da versão v2.0.0, o gcip-iap
exige a reescrita do aplicativo de interface personalizado usando o formato do SDK modular. Se você estiver migrando para o gcip-iap
v2.0.0 ou mais recente, siga estas etapas:
- Atualize a versão do
firebase
no arquivopackage.json
para v9.8.3 ou mais recente. - Atualize as instruções de importação
firebase
da seguinte maneira:
// Import Firebase modules.
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider } 'firebase/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
Como configurar a UI
Para configurar a interface, crie uma classe personalizada que implemente a
interface AuthenticationHandler
:
interface AuthenticationHandler {
languageCode?: string | null;
getAuth(apiKey: string, tenantId: string | null): FirebaseAuth;
startSignIn(auth: FirebaseAuth, match?: SelectedTenantInfo): Promise<UserCredential>;
selectTenant?(projectConfig: ProjectConfig, tenantIds: string[]): Promise<SelectedTenantInfo>;
completeSignOut(): Promise<void>;
processUser?(user: User): Promise<User>;
showProgressBar?(): void;
hideProgressBar?(): void;
handleError?(error: Error | CIAPError): void;
}
Durante a autenticação, a biblioteca chama automaticamente os métodos de AuthenticationHandler
.
Como selecionar locatários
Para selecionar um locatário, implemente selectTenant()
. Você
implementar esse método para escolher um locatário de maneira programática ou
exibir uma UI para que o próprio usuário possa selecionar uma.
Em ambos os casos, a biblioteca usa o objeto SelectedTenantInfo
retornado.
para concluir o fluxo de autenticação. Ele contém o ID do locatário selecionado, os IDs do provedor e o e-mail que o usuário digitou.
Se você tiver vários locatários no projeto, selecione um antes de autenticar um usuário. Se você tiver apenas um locatário ou estiver usando a autenticação para envolvidos no projeto, não será necessário implementar selectTenant()
.
O IAP aceita os mesmos provedores do Identity Platform, como:
- E-mail e senha
- OAuth (Google, Facebook, Twitter, GitHub, Microsoft etc.)
- SAML
- OIDC
- Número de telefone
- Personalizado
- Anônimo
Os tipos de autenticação por número de telefone, personalizada e anônima não são compatíveis com a multilocação.
Como selecionar locatários de maneira programática
Para selecionar um locatário de maneira programática, aproveite o contexto atual. O
A classe Authentication
contém getOriginalURL()
, que retorna o
URL que o usuário estava acessando antes da autenticação.
Use isso para localizar uma correspondência em uma lista de locatários associados:
// Select provider programmatically.
selectTenant(projectConfig, tenantIds) {
return new Promise((resolve, reject) => {
// Show UI to select the tenant.
auth.getOriginalURL()
.then((originalUrl) => {
resolve({
tenantId: getMatchingTenantBasedOnVisitedUrl(originalUrl),
// If associated provider IDs can also be determined,
// populate this list.
providerIds: [],
});
})
.catch(reject);
});
}
Permitir que os usuários selecionem locatários
Para permitir que o usuário selecione um locatário, mostre uma lista de locatários e peça para que ele escolha um ou peça que ele insira o endereço de e-mail e encontre um correspondente com base no domínio:
// Select provider by showing UI.
selectTenant(projectConfig, tenantIds) {
return new Promise((resolve, reject) => {
// Show UI to select the tenant.
renderSelectTenant(
tenantIds,
// On tenant selection.
(selectedTenantId) => {
resolve({
tenantId: selectedTenantId,
// If associated provider IDs can also be determined,
// populate this list.
providerIds: [],
// If email is available, populate this field too.
email: undefined,
});
});
});
}
Como autenticar usuários
Depois de criar um provedor, implemente getAuth()
para retornar uma instância Auth.
correspondentes à chave de API e ao ID de locatário fornecidos. Se nenhum ID de locatário
for fornecido, use provedores de identidade no nível do projeto.
getAuth()
rastreia onde o usuário correspondente ao
fornecida pelo cliente é armazenada. Além disso, ele permite atualizar silenciosamente o token de ID do Identity Platform de um usuário que já tenha sido autenticado anteriormente, sem exigir que esse usuário insira novamente as credenciais dele.
Se você estiver usando vários recursos do IAP com locatários diferentes, recomendamos usar uma instância de autenticação exclusiva para cada recurso. Desse modo, vários recursos com configurações diferentes poderão usar a mesma página de autenticação. Essa abordagem também permite que vários usuários façam login ao mesmo tempo sem desconectar o usuário anterior.
Veja a seguir um exemplo de como implementar o getAuth()
:
gcip-iap v1.0.0
getAuth(apiKey, tenantId) {
let auth = null;
// Make sure the expected API key is being used.
if (apiKey !== expectedApiKey) {
throw new Error('Invalid project!');
}
try {
auth = firebase.app(tenantId || undefined).auth();
// Tenant ID should be already set on initialization below.
} catch (e) {
// Use different App names for every tenant so that
// multiple users can be signed in at the same time (one per tenant).
const app = firebase.initializeApp(this.config, tenantId || '[DEFAULT]');
auth = app.auth();
// Set the tenant ID on the Auth instance.
auth.tenantId = tenantId || null;
}
return auth;
}
gcip-iap v2.0.0
import {initializeApp, getApp} from 'firebase/app';
import {getAuth} from 'firebase/auth';
getAuth(apiKey, tenantId) {
let auth = null;
// Make sure the expected API key is being used.
if (apiKey !== expectedApiKey) {
throw new Error('Invalid project!');
}
try {
auth = getAuth(getApp(tenantId || undefined));
// Tenant ID should be already set on initialization below.
} catch (e) {
// Use different App names for every tenant so that
// multiple users can be signed in at the same time (one per tenant).
const app = initializeApp(this.config, tenantId || '[DEFAULT]');
auth = getAuth(app);
// Set the tenant ID on the Auth instance.
auth.tenantId = tenantId || null;
}
return auth;
}
Como fazer login dos usuários
Para processar o login, implemente startSignIn()
e mostre uma
UI para a autenticação do usuário e, em seguida, retorna um
UserCredential
para o usuário conectado após a conclusão.
Em um ambiente multilocatário, é possível determinar os métodos de autenticação disponíveis
de SelectedTenantInfo
, se fornecido. Essa variável contém
as mesmas informações retornadas por selectTenant()
.
O exemplo a seguir mostra uma implementação de startSignIn()
para
um usuário atual com um e-mail e uma senha:
gcip-iap v1.0.0
startSignIn(auth, selectedTenantInfo) {
return new Promise((resolve, reject) => {
// Show the UI to sign-in or sign-up a user.
$('#sign-in-form').on('submit', (e) => {
const email = $('#email').val();
const password = $('#password').val();
// Example: Ask the user for an email and password.
// Note: The method of sign in may have already been determined from the
// selectedTenantInfo object.
auth.signInWithEmailAndPassword(email, password)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
});
}
gcip-iap v2.0.0
import {signInWithEmailAndPassword} from 'firebase/auth';
startSignIn(auth, selectedTenantInfo) {
return new Promise((resolve, reject) => {
// Show the UI to sign-in or sign-up a user.
$('#sign-in-form').on('submit', (e) => {
const email = $('#email').val();
const password = $('#password').val();
// Example: Ask the user for an email and password.
// Note: The method of sign in may have already been determined from the
// selectedTenantInfo object.
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
});
}
Também é possível fazer o login dos usuários com um provedor federado, como SAML ou OIDC, por meio de um pop-up ou redirecionamento:
gcip-iap v1.0.0
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign-in or sign-up a user.
return new Promise((resolve, reject) => {
// Provide the user multiple buttons to sign-in.
// For example sign-in with popup using a SAML provider.
// Note: The method of sign in may have already been determined from the
// selectedTenantInfo object.
const provider = new firebase.auth.SAMLAuthProvider('saml.myProvider');
auth.signInWithPopup(provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
// Using redirect flow. When the page redirects back and sign-in completes,
// ciap will detect the result and complete sign-in without any additional
// action.
auth.signInWithRedirect(provider);
});
}
gcip-iap v2.0.0
import {signInWithPopup, SAMLAuthProvider} from 'firebase/auth';
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign-in or sign-up a user.
return new Promise((resolve, reject) => {
// Provide the user multiple buttons to sign-in.
// For example sign-in with popup using a SAML provider.
// Note: The method of sign in might have already been determined from the
// selectedTenantInfo object.
const provider = new SAMLAuthProvider('saml.myProvider');
signInWithPopup(auth, provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
// Using redirect flow. When the page redirects back and sign-in completes,
// ciap will detect the result and complete sign-in without any additional
// action.
signInWithRedirect(auth, provider);
});
}
Alguns provedores de OAuth aceitam a transmissão de uma dica de login para conectar o usuário:
gcip-iap v1.0.0
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign-in or sign-up a user.
return new Promise((resolve, reject) => {
// Use selectedTenantInfo to determine the provider and pass the login hint
// if that provider supports it and the user specified an email address.
if (selectedTenantInfo &&
selectedTenantInfo.providerIds &&
selectedTenantInfo.providerIds.indexOf('microsoft.com') !== -1) {
const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.setCustomParameters({
login_hint: selectedTenantInfo.email || undefined,
});
} else {
// Figure out the provider used...
}
auth.signInWithPopup(provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
}
gcip-iap v2.0.0
import {signInWithPopup, OAuthProvider} from 'firebase/auth';
startSignIn(auth, selectedTenantInfo) {
// Show the UI to sign in or sign up a user.
return new Promise((resolve, reject) => {
// Use selectedTenantInfo to determine the provider and pass the login hint
// if that provider supports it and the user specified an email address.
if (selectedTenantInfo &&
selectedTenantInfo.providerIds &&
selectedTenantInfo.providerIds.indexOf('microsoft.com') !== -1) {
const provider = new OAuthProvider('microsoft.com');
provider.setCustomParameters({
login_hint: selectedTenantInfo.email || undefined,
});
} else {
// Figure out the provider used...
}
signInWithPopup(auth, provider)
.then((userCredential) => {
resolve(userCredential);
})
.catch((error) => {
// Show the error message.
});
});
}
Para mais informações, consulte Como autenticar com multiplicidade de locações.
Como processar os erros
Para exibir mensagens de erro aos usuários ou para tentar a recuperação de erros como tempos limite de rede,
implementar handleError()
.
O exemplo abaixo implementa handleError()
:
handleError(error) {
showAlert({
code: error.code,
message: error.message,
// Whether to show the retry button. This is only available if the error is
// recoverable via retrial.
retry: !!error.retry,
});
// When user clicks retry, call error.retry();
$('.alert-link').on('click', (e) => {
error.retry();
e.preventDefault();
return false;
});
}
A tabela abaixo lista códigos de erro específicos do IAP que podem
serão retornadas. O Identity Platform também pode retornar erros. consulte a documentação
firebase.auth.Auth
Código do erro | Descrição |
---|---|
invalid-argument |
O cliente especificou um argumento inválido. |
failed-precondition |
A solicitação não pode ser executada no estado atual do sistema. |
out-of-range |
O cliente especificou um intervalo inválido. |
unauthenticated |
Solicitação não autenticada devido a um token de OAuth ausente, inválido ou expirado. |
permission-denied |
O cliente não tem permissão suficiente ou a IU está hospedada em um domínio não autorizado. |
not-found |
O recurso especificado não foi encontrado. |
aborted |
Conflito de simultaneidade, como leitura-modificação-gravação. |
already-exists |
O recurso que um cliente tentou criar já existe. |
resource-exhausted |
Excedeu a cota de recursos ou está perto de atingir a limitação de taxa. |
cancelled |
Solicitação cancelada pelo cliente. |
data-loss |
Perda ou corrupção de dados irrecuperável. |
unknown |
Erro desconhecido de servidor. |
internal |
Erro interno do servidor. |
not-implemented |
Método da API não implementado pelo servidor. |
unavailable |
Serviço indisponível. |
restart-process |
Acesse novamente o URL que redirecionou você para esta página para reiniciar o processo de autenticação. |
deadline-exceeded |
O prazo de solicitação foi excedido. |
authentication-uri-fail |
Falha ao gerar o URI de autenticação. |
gcip-token-invalid |
Token de ID do GCIP fornecido inválido. |
gcip-redirect-invalid |
URL de redirecionamento inválido. |
get-project-mapping-fail |
Falha ao receber o ID do projeto. |
gcip-id-token-encryption-error |
Erro de criptografia do token de ID do GCIP. |
gcip-id-token-decryption-error |
Erro de descriptografia do token de ID do GCIP. |
gcip-id-token-unescape-error |
Falha na função unescape de base64 segura para a Web. |
resource-missing-gcip-sign-in-url |
O URL de autenticação do GCIP está ausente para o recurso especificado do IAP. |
Personalizar a interface
É possível personalizar a página de autenticação com recursos opcionais, como barras de progresso e páginas de desativação.
Como exibir uma IU de progresso
Para mostrar uma IU de progresso personalizada ao usuário sempre que o módulo gcip-iap
executar tarefas de rede de longa duração,
implemente showProgressBar()
e hideProgressBar()
.
Como desconectar os usuários
Em alguns casos, convém permitir que os usuários saiam de todas as sessões atuais que compartilham o mesmo URL de autenticação.
Depois que um usuário sai, talvez não haja um URL para redirecioná-lo de volta.
Isso geralmente ocorre quando um usuário sai de todos os locatários associados a uma
página de login. Nesse caso, implemente completeSignOut()
para
exibirá uma mensagem indicando que o usuário saiu com sucesso. Se você não implementar esse método,
uma página em branco aparece quando um usuário sai.
Processando usuários
Para modificar um usuário conectado antes de redirecionar para o recurso do IAP,
implemente processUser()
.
Você pode usar esse método para fazer o seguinte:
- vincular a outros provedores;
- atualizar o perfil do usuário;
- pedir mais dados do usuário após o registro;
- processar tokens de acesso de OAuth retornados por
getRedirectResult()
depois de chamarsignInWithRedirect()
.
Veja abaixo um exemplo de como implementar o processUser()
:
gcip-iap v1.0.0
processUser(user) {
return lastAuthUsed.getRedirectResult().then(function(result) {
// Save additional data, or ask the user for additional profile information
// to store in database, etc.
if (result) {
// Save result.additionalUserInfo.
// Save result.credential.accessToken for OAuth provider, etc.
}
// Return the user.
return user;
});
}
gcip-iap v2.0.0
import {getRedirectResult} from 'firebase/auth';
processUser(user) {
return getRedirectResult(lastAuthUsed).then(function(result) {
// Save additional data, or ask the user for additional profile information
// to store in database, etc.
if (result) {
// Save result.additionalUserInfo.
// Save result.credential.accessToken for OAuth provider, etc.
}
// Return the user.
return user;
});
}
Se você quiser que as mudanças em um usuário sejam refletidas nas declarações do token de ID. propagadas pelo IAP para seu aplicativo, será preciso forçar a token a ser atualizado:
gcip-iap v1.0.0
processUser(user) {
return user.updateProfile({
photoURL: 'https://example.com/profile/1234/photo.png',
}).then(function() {
// To reflect updated photoURL in the ID token, force token
// refresh.
return user.getIdToken(true);
}).then(function() {
return user;
});
}
gcip-iap v2.0.0
import {updateProfile} from 'firebase/auth';
processUser(user) {
return updateProfile(user, {
photoURL: 'https://example.com/profile/1234/photo.png',
}).then(function() {
// To reflect updated photoURL in the ID token, force token
// refresh.
return user.getIdToken(true);
}).then(function() {
return user;
});
}
Como testar a interface
Depois de criar uma classe que implemente AuthenticationHandler
, você poderá
Use-o para criar uma nova instância do Authentication
e inicie-a:
// Implement interface AuthenticationHandler.
// const authHandlerImplementation = ....
const ciapInstance = new ciap.Authentication(authHandlerImplementation);
ciapInstance.start();
Implante seu aplicativo e navegue até a página de autenticação. Você verá sua IU de login personalizada.
A seguir
- Saiba como acessar recursos que não são do Google de maneira programática.
- Saiba como gerenciar sessões.
- Saiba como identidades externas funcionam com o IAP.