En este artículo, se muestra cómo compilar tu propia página de autenticación con IAP y identidades externas. Si creas esta página por tu cuenta, tendrás control total sobre el flujo de autenticación y la experiencia del usuario.
Si no necesitas personalizar completamente tu IU, puedes permitir que IAP aloje una página de acceso o usar FirebaseUI para obtener una experiencia más optimizada.
Descripción general
Para crear tu propia página de autenticación, sigue estos pasos:
- Habilita identidades externas. Selecciona Yo proporcionaré mi propia opción de IU durante la configuración.
- Instala la biblioteca
gcip-iap
. - Configura la IU implementando la interfaz
AuthenticationHandler
. Tu página de autenticación debe manejar las siguientes situaciones:- Selección de usuarios
- Autorización de usuarios
- Acceso de usuarios
- Manejo de errores
- Opcional: Personaliza tu página de autenticación con funciones adicionales, como barras de progreso, páginas de salida y procesamiento de los usuarios.
- Prueba tu IU.
Instala la biblioteca gcip-iap
Para instalar la biblioteca gcip-iap
, ejecuta el siguiente comando:
npm install gcip-iap --save
El módulo de Administración de socios de red gcip-iap
simplifica las comunicaciones entre tu aplicación, IAP y Identity Platform. Esto te permite personalizar toda la
flujo de autenticación sin tener que administrar los intercambios subyacentes entre
IU e IAP.
Usa las importaciones correctas para tu versión del SDK:
gcip-iap v0.1.4 o versiones anteriores
// 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';
De gcip-iap v1.0.0 a v1.1.0
A partir de la versión 1.0.0, gcip-iap
requiere la dependencia de pares firebase
v9 o una posterior.
Si migras a gcip-iap
v1.0.0 o superior, completa lo siguiente:
acciones:
- Actualiza la versión de
firebase
en tu archivopackage.json
a la 9.6.0 (o una posterior). - Actualiza las sentencias de importación
firebase
de la siguiente manera:
// Import Firebase modules.
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
No es necesario realizar cambios adicionales en el código.
gcip-iap v2.0.0
A partir de la versión 2.0.0, gcip-iap
requiere que vuelvas a escribir tu aplicación de IU personalizada con el formato de SDK modular. Si migras a gcip-iap
v2.0.0 o una versión posterior, completa las siguientes acciones:
- Actualiza la versión de
firebase
en tu archivopackage.json
a la 9.8.3 (o una posterior). - Actualiza las declaraciones de importación de
firebase
de la siguiente manera:
// Import Firebase modules.
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider } 'firebase/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
Configura la IU
Para configurar la IU, crea una clase personalizada que implemente la interfaz 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 la autenticación, la biblioteca llama automáticamente a los métodos de AuthenticationHandler
.
Cómo seleccionar usuarios
Para seleccionar un usuario, implementa selectTenant()
. Puedes implementar este método para elegir un usuario de manera programática o mostrar una IU para que el usuario pueda seleccionarlo.
En cualquier caso, la biblioteca usa el objeto SelectedTenantInfo
que se muestra.
para completar el flujo de autenticación. Contiene el ID del usuario seleccionado, los ID de cualquier proveedor y el correo electrónico que ingresó el usuario.
Si tienes varios usuarios en tu proyecto, debes seleccionar uno antes de poder
para autenticar a un usuario. Si solo tienes un usuario o usas
autenticación a nivel de proyecto, no necesitas implementar selectTenant()
.
IAP es compatible con los mismos proveedores que Identity Platform, por ejemplo:
- Correo electrónico y contraseña
- OAuth (Google, Facebook, Twitter, GitHub, Microsoft, etcétera)
- SAML
- OIDC
- Número de teléfono
- Personalizados
- Anónimo
Los tipos de autenticación personalizada, anónima y por número de teléfono no son compatibles con los multiusuarios.
Selecciona usuarios de manera programática
Para seleccionar un usuario de manera programática, aprovecha el contexto actual. El
La clase Authentication
contiene getOriginalURL()
que muestra el
URL a la que accedió el usuario antes de la autenticación.
Usa esto para Ubica una coincidencia de una lista de usuarios asociados:
// 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);
});
}
Permite que los usuarios seleccionen usuarios
Para permitir que el usuario seleccione un usuario, muestra una lista de usuarios y haz que el usuario elija uno, o pídele que ingrese su dirección de correo electrónico y, luego, busque una coincidencia basada en el dominio:
// 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,
});
});
});
}
Cómo autenticar usuarios
Después de tener un proveedor, implementa getAuth()
para mostrar una instancia de Auth.
correspondiente a la clave de API y el ID de usuario proporcionados. Si no tienes un ID de usuario
usa proveedores de identidad a nivel de proyecto.
getAuth()
realiza un seguimiento de dónde se almacena el usuario correspondiente a la configuración proporcionada. También permite actualizar de forma silenciosa un token de ID de Identity Platform de un usuario autenticado anteriormente sin necesidad de que el usuario vuelva a ingresar sus credenciales.
Si usas varios recursos de IAP con distintos usuarios, te recomendamos que uses una instancia de autenticación única para cada recurso. Esto permite que varios recursos con diferentes configuraciones usen la misma página de autenticación. También permite que varios usuarios accedan al mismo tiempo sin cerrar la sesión del usuario anterior.
El siguiente es un ejemplo de cómo implementar 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;
}
Permite que los usuarios accedan
Para administrar el acceso, implementa startSignIn()
, muestra un
IU para que el usuario se autentique y, luego, devolverá un
UserCredential
para el usuario que accedió.
En un entorno multiusuario, puedes determinar los métodos de autenticación disponibles
desde SelectedTenantInfo
, si se proporcionó. Esta variable contiene
la misma información que muestra selectTenant()
.
En el siguiente ejemplo, se muestra una implementación de startSignIn()
para
un usuario existente con un correo electrónico y una contraseña:
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.
});
});
});
}
También puedes acceder a los usuarios con un proveedor federado, como OIDC o SAML, mediante una ventana emergente o un redireccionamiento:
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);
});
}
Algunos proveedores de OAuth admiten la aprobación de una sugerencia de acceso para acceder:
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.
});
});
}
Consulta Autenticación con multiusuarios. para obtener más información.
Maneja los errores
Para mostrar mensajes de error a los usuarios o para intentar recuperarse de errores, como los tiempos de espera de la red, implementa handleError()
.
En el siguiente ejemplo, se 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;
});
}
En la siguiente tabla, se enumeran los códigos de error específicos de IAP que pueden
se devuelvan. Identity Platform también puede mostrar errores. consulta la documentación
firebase.auth.Auth
Código de error | Descripción |
---|---|
invalid-argument |
El cliente especificó un argumento no válido. |
failed-precondition |
La solicitud no se puede ejecutar en el estado actual del sistema. |
out-of-range |
El cliente especificó un rango no válido. |
unauthenticated |
La solicitud no se autenticó debido a que falta un token de OAuth, no es válido o venció. |
permission-denied |
El cliente no tiene permiso suficiente o la IU está alojada en un dominio no autorizado. |
not-found |
No se encontró el recurso especificado. |
aborted |
Conflicto de concurrencia, como conflicto de lectura-modificación-escritura. |
already-exists |
El recurso que el cliente intentó crear ya existe. |
resource-exhausted |
Sin cuota de recursos o a punto de alcanzar el límite de frecuencia. |
cancelled |
El cliente canceló la solicitud. |
data-loss |
Daño o pérdida de datos no recuperable. |
unknown |
Error de servidor desconocido. |
internal |
Error interno del servidor |
not-implemented |
El servidor no implementó el método de la API. |
unavailable |
Servicio no disponible |
restart-process |
Revisa la URL que te redireccionó a esta página para reiniciar el proceso de autenticación. |
deadline-exceeded |
Se excedió el plazo de la solicitud. |
authentication-uri-fail |
No se pudo generar el URI de autenticación. |
gcip-token-invalid |
Se proporcionó un token de ID de GCIP no válido. |
gcip-redirect-invalid |
URL de redireccionamiento no válida |
get-project-mapping-fail |
No se pudo obtener el ID del proyecto. |
gcip-id-token-encryption-error |
Error de encriptación del token de ID de GCIP. |
gcip-id-token-decryption-error |
Error de desencriptación del token de ID de GCIP. |
gcip-id-token-unescape-error |
Se produjo un error sin escape de base64 seguro para la Web. |
resource-missing-gcip-sign-in-url |
Falta la URL de autenticación GCIP para el recurso IAP especificado. |
Personaliza la IU
Puedes personalizar tu página de autenticación con funciones opcionales como las páginas para salir de tu cuenta.
Muestra una IU de progreso
Para mostrar una IU de progreso personalizada al usuario cada vez que el módulo gcip-iap
ejecute tareas de red de larga duración, implementa showProgressBar()
y hideProgressBar()
.
Cierre de sesión de los usuarios
En algunos casos, es posible que desees permitir que los usuarios salgan de todas las sesiones actuales que comparten la misma URL de autenticación.
Después de que un usuario sale, es posible que no haya una URL a la cual redirigirlos.
Esto suele ocurrir cuando un usuario sale de todas las instancias asociadas con una página de acceso. En este caso, implementa completeSignOut()
para
mostrar un mensaje que indique que el usuario salió correctamente. Si no implementas este método,
y, cuando un usuario sale de su cuenta, aparece una página en blanco.
Usuarios del procesamiento
Para modificar un usuario que accedió antes de que se lo redireccione al recurso de IAP, haz lo siguiente:
implementar processUser()
.
Puedes usar este método para lo siguiente:
- Vincular a proveedores adicionales
- Actualizar el perfil del usuario
- Solicitar al usuario datos adicionales después del registro
- Procesar tokens de acceso OAuth que muestra
getRedirectResult()
después de llamar asignInWithRedirect()
.
El siguiente es un ejemplo de implementación de 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;
});
}
Si quieres que los cambios de un usuario se reflejen en las reclamaciones de tokens de ID propagado por IAP a tu app, debes forzar la token para actualizar:
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;
});
}
Prueba la IU
Después de crear una clase que implemente AuthenticationHandler
, puedes
Úsala para crear una nueva instancia de Authentication
y, luego, iniciarla:
// Implement interface AuthenticationHandler.
// const authHandlerImplementation = ....
const ciapInstance = new ciap.Authentication(authHandlerImplementation);
ciapInstance.start();
Implementa tu aplicación y navega a la página de autenticación. Deberías ver tu IU de acceso personalizada.
¿Qué sigue?
- Obtén más información sobre cómo acceder de manera programática a los recursos ajenos a Google.
- Obtén más información sobre cómo administrar sesiones.
- Aprende cómo funcionan las identidades externas con IAP.