Cet article explique comment créer votre propre page d'authentification à l'aide d'identités externes et d'IAP. Créer vous-même cette page vous permet de contrôler entièrement le flux d'authentification et l'expérience utilisateur.
Si vous n'avez pas besoin de personnaliser entièrement votre interface utilisateur, vous pouvez laisser IAP héberger une page de connexion ou utiliser FirebaseUI pour une expérience plus fluide.
Présentation
Pour créer votre propre page d'authentification, procédez comme suit:
- Activez les identités externes. Sélectionnez l'option Je fournirai ma propre interface utilisateur lors de la configuration.
- Installez la bibliothèque
gcip-iap
. - Configurez l'UI en implémentant l'interface
AuthenticationHandler
. Votre page d'authentification doit gérer les scénarios suivants :- La sélection de locataires
- Autorisation des utilisateurs
- Connexion de l'utilisateur
- Gestion des exceptions
- Facultatif: personnalisez votre page d'authentification avec des fonctionnalités supplémentaires, telles que des barres de progression, des pages de déconnexion et le traitement des utilisateurs.
- Testez votre UI.
Installer la bibliothèque gcip-iap
Pour installer la bibliothèque gcip-iap
, exécutez la commande suivante:
npm install gcip-iap --save
Le module NPM gcip-iap
fait abstraction des communications entre votre application, IAP et Identity Platform. Cela vous permet de personnaliser l'ensemble du processus d'authentification sans avoir à gérer les échanges sous-jacents entre l'UI et l'IAP.
Utilisez les importations appropriées pour votre version de SDK:
gcip-iap v0.1.4 ou version antérieure
// 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 à v1.1.0
À partir de la version 1.0.0, gcip-iap
nécessite la dépendance de paire firebase
v9 ou ultérieure.
Si vous migrez vers la version 1.0.0 ou ultérieure de gcip-iap
, procédez comme suit:
- Mettez à jour la version
firebase
dans votre fichierpackage.json
vers la version 9.6.0 ou ultérieure. - Mettez à jour les instructions d'importation
firebase
comme suit:
// Import Firebase modules.
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
Aucune modification de code supplémentaire n'est nécessaire.
gcip-iap v2.0.0
À partir de la version 2.0.0, gcip-iap
nécessite la réécriture de votre application d'UI personnalisée à l'aide du format de SDK modulaire. Si vous migrez vers la version 2.0.0 ou ultérieure de gcip-iap
, procédez comme suit:
- Mettez à jour la version
firebase
dans votre fichierpackage.json
vers la version 9.8.3 ou ultérieure. - Mettez à jour les instructions d'importation
firebase
comme suit:
// Import Firebase modules.
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider } 'firebase/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';
Configurer l'interface utilisateur
Pour configurer l'UI, créez une classe personnalisée qui implémente l'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;
}
Lors de l'authentification, la bibliothèque appelle automatiquement les méthodes de AuthenticationHandler
.
Sélectionner des locataires
Pour sélectionner un locataire, implémentez selectTenant()
. Vous pouvez mettre en œuvre cette méthode pour choisir un locataire par programmation ou afficher une UI afin que l'utilisateur puisse en sélectionner un lui-même.
Dans les deux cas, la bibliothèque utilise l'objet SelectedTenantInfo
renvoyé pour terminer le processus d'authentification. Il contient l'ID du locataire sélectionné, les éventuels ID de fournisseur et l'e-mail saisi par l'utilisateur.
Si votre projet comporte plusieurs locataires, vous devez en sélectionner un avant de pouvoir authentifier un utilisateur. Si vous n'avez qu'un seul locataire ou si vous utilisez l'authentification au niveau du projet, vous n'avez pas besoin d'implémenter selectTenant()
.
IAP est compatible avec les mêmes fournisseurs qu'Identity Platform, par exemple :
- Adresse e-mail et mot de passe
- OAuth (Google, Facebook, Twitter, GitHub, Microsoft, etc.)
- SAML
- OIDC
- N° de téléphone
- Personnalisé
- Anonyme
Les types d'authentification par numéro de téléphone, personnalisée et anonyme ne sont pas compatibles avec l'architecture mutualisée.
Sélectionner des locataires par programmation
Pour sélectionner un locataire par programmation, utilisez le contexte actuel. La classe Authentication
contient getOriginalURL()
qui renvoie l'URL à laquelle l'utilisateur accédait avant l'authentification.
Utilisez cette méthode pour trouver une correspondance dans une liste de locataires associés:
// 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);
});
}
Autoriser les utilisateurs à sélectionner des locataires
Pour permettre à l'utilisateur de sélectionner un locataire, affichez une liste de locataires et demandez-lui d'en choisir un, ou demandez-lui de saisir son adresse e-mail, puis de rechercher une correspondance en fonction du domaine:
// 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,
});
});
});
}
Authentifier les utilisateurs
Une fois que vous avez un fournisseur, implémentez getAuth()
pour renvoyer une instance Auth correspondant à la clé API et à l'ID de locataire fournis. Si aucun ID de locataire n'est fourni, utilisez des fournisseurs d'identité au niveau du projet.
getAuth()
permet de savoir où est stocké l'utilisateur correspondant à la configuration fournie. Il permet également d'actualiser silencieusement un jeton d'ID Identity Platform d'un utilisateur précédemment authentifié, sans que celui-ci doive saisir de nouveau ses identifiants.
Si vous utilisez plusieurs ressources IAP avec différents locataires, nous vous recommandons d'utiliser une instance d'authentification unique pour chaque ressource. Cela permet à plusieurs ressources ayant des configurations différentes d'utiliser la même page d'authentification. Cela permet également à plusieurs utilisateurs de se connecter en même temps sans déconnecter l'utilisateur précédent.
Vous trouverez ci-dessous un exemple de mise en œuvre de 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;
}
Procéder à la connexion des utilisateurs
Pour gérer la connexion, implémentez startSignIn()
, affichez une UI permettant à l'utilisateur de s'authentifier, puis renvoyez un objet UserCredential
pour l'utilisateur connecté une fois l'opération terminée.
Dans un environnement multi-tenant, vous pouvez déterminer les méthodes d'authentification disponibles à partir de SelectedTenantInfo
, si cette variable a été fournie. Cette variable contient les mêmes informations que celles renvoyées par selectTenant()
.
L'exemple suivant montre une implémentation de startSignIn()
pour un utilisateur existant avec une adresse e-mail et un mot de passe:
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.
});
});
});
}
Vous pouvez également connecter des utilisateurs avec un fournisseur fédéré, comme SAML ou OIDC, à l'aide d'un pop-up ou d'une redirection :
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);
});
}
Certains fournisseurs OAuth acceptent la transmission d'un indice de connexion pour la connexion :
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.
});
});
}
Pour en savoir plus, consultez la section Authentification avec la multi-propriété.
Traiter les erreurs
Pour afficher des messages d'erreur aux utilisateurs ou pour tenter de résoudre des erreurs telles que les expirations réseau, implémentez handleError()
.
L'exemple suivant implémente 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;
});
}
Le tableau ci-dessous répertorie les codes d'erreur spécifiques à l'IAP qui peuvent être renvoyés. Identity Platform peut également renvoyer des erreurs. Consultez la documentation concernant firebase.auth.Auth
.
Code d'erreur | Description |
---|---|
invalid-argument |
Le client a spécifié un argument incorrect. |
failed-precondition |
La requête ne peut pas être exécutée dans l'état actuel du système. |
out-of-range |
Le client a spécifié une plage non valide. |
unauthenticated |
La requête n'a pas été authentifiée en raison d'un jeton OAuth manquant, non valide ou ayant expiré. |
permission-denied |
Le client ne dispose pas d'une autorisation suffisante, ou l'UI est hébergée sur un domaine non autorisé. |
not-found . |
Ressource indiquée introuvable. |
aborted |
Un conflit de simultanéité existe, tel qu'un conflit lecture-modification-écriture. |
already-exists |
La ressource qu'un client a essayé de créer existe déjà. |
resource-exhausted |
Le quota de ressources est dépassé ou la limite du débit est atteinte. |
cancelled |
La demande a été annulée par le client. |
data-loss |
Perte de données irrécupérable ou corruption de données. |
unknown |
Erreur du serveur inconnue. |
internal |
Erreur interne du serveur. |
not-implemented |
Méthode d'API non mise en œuvre par le serveur. |
unavailable |
Service indisponible. |
restart-process |
Accédez de nouveau à l'URL qui vous a redirigé vers cette page pour relancer le processus d'authentification. |
deadline-exceeded |
Délai de requête dépassé. |
authentication-uri-fail |
Échec de la génération de l'URI d'authentification. |
gcip-token-invalid |
Le jeton d'ID GCIP fourni n'est pas valide. |
gcip-redirect-invalid |
L'URL de redirection n'est pas valide. |
get-project-mapping-fail |
Échec de l'obtention de l'ID du projet. |
gcip-id-token-encryption-error |
Erreur de chiffrement du jeton d'ID GCIP. |
gcip-id-token-decryption-error |
Erreur de déchiffrement du jeton d'ID GCIP. |
gcip-id-token-unescape-error |
Échec de la suppression de l'échappement en base64 adapté au Web. |
resource-missing-gcip-sign-in-url |
URL d'authentification GCIP manquante pour la ressource IAP spécifiée. |
Personnaliser l'UI
Vous pouvez personnaliser votre page d'authentification avec des fonctionnalités facultatives telles que des barres de progression et des pages de déconnexion.
Afficher une UI de progression
Pour afficher une UI de progression personnalisée pour l'utilisateur lorsque le module gcip-iap
exécute des tâches réseau de longue durée, implémentez showProgressBar()
et hideProgressBar()
.
Déconnecter des utilisateurs
Dans certains cas, vous pouvez autoriser les utilisateurs à se déconnecter de toutes les sessions en cours qui partagent la même URL d'authentification.
Lorsqu'un utilisateur se déconnecte, il est possible qu'il n'y ait aucune URL vers laquelle le rediriger.
Cela se produit généralement lorsqu'un utilisateur se déconnecte de tous les locataires associés à une page de connexion. Dans ce cas, implémentez completeSignOut()
pour afficher un message indiquant que l'utilisateur s'est bien déconnecté. Si vous n'implémentez pas cette méthode, une page vide s'affiche lorsqu'un utilisateur se déconnecte.
Traiter les utilisateurs
Pour modifier un utilisateur connecté avant de le rediriger vers la ressource IAP, implémentez processUser()
.
Vous pouvez utiliser cette méthode pour effectuer les opérations suivantes:
- Établir une association à des fournisseurs supplémentaires
- Mettre à jour le profil de l'utilisateur
- Demander à l'utilisateur de fournir des données supplémentaires après l'inscription
- Traiter les jetons d'accès OAuth renvoyés par
getRedirectResult()
après avoir appelésignInWithRedirect()
Voici un exemple de mise en œuvre 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 vous souhaitez que les modifications apportées à un utilisateur apparaissent dans les revendications de jeton d'ID propagées par IAP vers votre application, vous devez forcer l'actualisation du jeton:
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;
});
}
Tester l'UI
Une fois que vous avez créé une classe qui met en œuvre AuthenticationHandler
, vous pouvez l'utiliser pour créer une instance Authentication
et la démarrer:
// Implement interface AuthenticationHandler.
// const authHandlerImplementation = ....
const ciapInstance = new ciap.Authentication(authHandlerImplementation);
ciapInstance.start();
Déployez votre application et accédez à la page d'authentification. Votre UI de connexion personnalisée doit s'afficher.
Étape suivante
- Découvrez comment accéder aux ressources non-Google par programmation.
- Apprenez-en plus sur la gestion des sessions.
- Découvrez comment les identités externes fonctionnent avec IAP.