Benutzerdefinierte Anmeldeseite erstellen

In diesem Artikel wird beschrieben, wie Sie mit externen Identitäten und IAP eine eigene Authentifizierungsseite erstellen. Wenn Sie diese Seite selbst erstellen, haben Sie die volle Kontrolle über den Authentifizierungsablauf und die Nutzerfreundlichkeit.

Wenn Sie die Benutzeroberfläche nicht vollständig anpassen müssen, können Sie eine Anmeldeseite von IAP hosten lassen oder FirebaseUI verwenden, um die Nutzerfreundlichkeit zu verbessern.

Übersicht

So erstellen Sie eine eigene Authentifizierungsseite:

  1. Externe Identitäten aktivieren Wählen Sie bei der Einrichtung die Option Eigene Benutzeroberfläche bereitstellen aus.
  2. Installieren Sie die gcip-iap-Bibliothek.
  3. Benutzeroberfläche durch Implementieren der AuthenticationHandler-Schnittstelle konfigurieren Ihre Authentifizierungsseite muss die folgenden Szenarien abdecken:
    • Mandantenauswahl
    • Nutzerautorisierung
    • Nutzeranmeldung
    • Fehlerbehandlung
  4. Optional: Authentifizierungsseite mit zusätzlichen Funktionen anpassen, z. B. Fortschrittsanzeigen, Abmeldeseiten und Nutzerverarbeitung.
  5. Benutzeroberfläche testen:

gcip-iap-Bibliothek installieren

Führen Sie den folgenden Befehl aus, um die gcip-iap-Bibliothek zu installieren:

npm install gcip-iap --save

Das NPM-Modul gcip-iap abstrahiert die Kommunikation zwischen Ihrer Anwendung, IAP und Identity Platform. So können Sie den gesamten Authentifizierungsablauf anpassen, ohne die zugrunde liegenden Interaktionen zwischen der Benutzeroberfläche und IAP verwalten zu müssen.

Verwenden Sie die richtigen Importe für Ihre SDK-Version:

gcip-iap v0.1.4 oder früher

// 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 bis v1.1.0

Ab Version v1.0.0 erfordert gcip-iap die Peer-Abhängigkeit firebase v9 oder höher. Wenn Sie zu gcip-iap v1.0.0 oder höher migrieren, führen Sie die folgenden Schritte aus:

  • Aktualisieren Sie die firebase-Version in Ihrer package.json-Datei auf v9.6.0 oder höher.
  • Aktualisieren Sie die firebase-Importanweisungen so:
// Import Firebase modules.
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// Import the gcip-iap module.
import * as ciap from 'gcip-iap';

Es sind keine zusätzlichen Codeänderungen erforderlich.

gcip-iap v2.0.0

Ab Version 2.0.0 erfordert gcip-iap, dass Sie Ihre benutzerdefinierte UI-Anwendung mit dem modularen SDK-Format neu schreiben. Wenn Sie zu gcip-iap v2.0.0 oder höher migrieren, führen Sie die folgenden Aktionen aus:

  • Aktualisieren Sie die firebase-Version in Ihrer package.json-Datei auf v9.8.3 oder höher.
  • Aktualisieren Sie die firebase-Importanweisungen so:
  // Import Firebase modules.
  import { initializeApp } from 'firebase/app';
  import { getAuth, GoogleAuthProvider } 'firebase/auth';
  // Import the gcip-iap module.
  import * as ciap from 'gcip-iap';

Benutzeroberfläche konfigurieren

Erstellen Sie zum Konfigurieren der Benutzeroberfläche eine benutzerdefinierte Klasse, die die AuthenticationHandler-Schnittstelle implementiert:

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

Während der Authentifizierung ruft die Bibliothek automatisch die Methoden von AuthenticationHandler auf.

Mandanten auswählen

Implementieren Sie selectTenant(), um einen Mandanten auszuwählen. Sie können diese Methode implementieren, um einen Mandanten programmatisch auszuwählen oder eine Benutzeroberfläche anzuzeigen, damit der Nutzer selbst einen Mandanten auswählen kann.

In beiden Fällen verwendet die Bibliothek das zurückgegebene SelectedTenantInfo-Objekt, um den Authentifizierungsablauf abzuschließen. Es enthält die ID des ausgewählten Mandanten, alle Anbieter-IDs und die E-Mail-Adresse, die der Nutzer eingegeben hat.

Wenn Sie mehrere Mandanten in Ihrem Projekt haben, müssen Sie einen auswählen, bevor Sie einen Nutzer authentifizieren können. Wenn Sie nur einen Mandanten haben oder die Authentifizierung auf Projektebene verwenden, müssen Sie selectTenant() nicht implementieren.

IAP unterstützt die gleichen Anbieter wie Identity Platform:

  • E-Mail-Adresse und Passwort
  • OAuth (Google, Facebook, Twitter, GitHub, Microsoft usw.)
  • SAML
  • OIDC
  • Telefonnummer
  • Benutzerdefiniert
  • Anonym

Die Authentifizierungstypen „Telefonnummer“, „Benutzerdefiniert“ und „Anonym“ werden für Multi-Tenancy nicht unterstützt.

Mandanten programmatisch auswählen

Um einen Mandanten programmatisch auszuwählen, nutzen Sie den aktuellen Kontext. Die Klasse Authentication enthält getOriginalURL(), das die URL zurückgibt, auf die der Nutzer vor der Authentifizierung zugegriffen hat.

So finden Sie eine Übereinstimmung in einer Liste verknüpfter Mandanten:

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

Nutzern erlauben, Mandanten auszuwählen

Damit der Nutzer einen Mandanten auswählen kann, zeigen Sie eine Liste der Mandanten an und lassen Sie den Nutzer einen auswählen. Alternativ können Sie den Nutzer auch bitten, seine E‑Mail-Adresse einzugeben, und dann anhand der Domain eine Übereinstimmung suchen:

// 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,
          });
        });
  });
}

Nutzer authentifizieren

Nachdem Sie einen Anbieter haben, implementieren Sie getAuth(), um eine Auth-Instanz zurückzugeben, die dem angegebenen API-Schlüssel und der angegebenen Mandanten-ID entspricht. Wenn keine Mandanten-ID angegeben ist, verwenden Sie Identitätsanbieter auf Projektebene.

getAuth() gibt an, wo der Nutzer, der der bereitgestellten Konfiguration entspricht, gespeichert ist. Außerdem ist es damit möglich, das Identity Platform-ID-Token eines zuvor authentifizierten Nutzers im Hintergrund zu aktualisieren, ohne dass der Nutzer seine Anmeldedaten noch einmal eingeben muss.

Wenn Sie mehrere IAP-Ressourcen mit unterschiedlichen Mandanten verwenden, empfehlen wir, für jede Ressource eine eindeutige Authentifizierungsinstanz zu verwenden. Dadurch können Sie mehrere Ressourcen mit unterschiedlichen Konfigurationen die gleiche Authentifizierungsseite nutzen. Außerdem bietet es mehreren Nutzern die Möglichkeit, sich gleichzeitig anzumelden, ohne dass der vorherige Nutzer abgemeldet werden muss.

Im Folgenden finden Sie ein Beispiel für die Implementierung von 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;
}

Nutzer anmelden

Implementieren Sie startSignIn(), um die Anmeldung zu verarbeiten, zeigen Sie eine Benutzeroberfläche für die Authentifizierung des Nutzers an und geben Sie dann nach Abschluss ein UserCredential für den angemeldeten Nutzer zurück.

In einer Mehrmandantenumgebung können Sie die verfügbaren Authentifizierungsmethoden über SelectedTenantInfo ermitteln, sofern diese bereitgestellt wurde. Diese Variable enthält dieselben Informationen wie selectTenant().

Das folgende Beispiel zeigt eine startSignIn()-Implementierung für einen vorhandenen Nutzer mit einer E-Mail-Adresse und einem Passwort:

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

Sie haben auch die Möglichkeit, Nutzer mit einem Föderationsanbieter wie SAML oder OIDC über ein Pop-up-Fenster oder eine Weiterleitung anzumelden:

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

Einige OAuth-Anbieter unterstützen die Übergabe eines Hinweises für die Anmeldung:

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

Weitere Informationen finden Sie unter Authentifizierung mit Mandantenfähigkeit.

Fehlerbehebung

Implementieren Sie handleError(), um Nutzern Fehlermeldungen anzuzeigen oder Fehler wie Netzwerk-Timeouts zu beheben.

Im folgenden Beispiel wird handleError() implementiert:

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

In der folgenden Tabelle sind IAP-spezifische Fehlercodes aufgeführt, die zurückgegeben werden können. Identity Platform kann auch Fehler zurückgeben. Weitere Informationen finden Sie in der Dokumentation zu firebase.auth.Auth.

Fehlercode Beschreibung
invalid-argument Der Client hat ein ungültiges Argument angegeben.
failed-precondition Die Anfrage kann im aktuellen Systemzustand nicht ausgeführt werden.
out-of-range Der Client hat einen ungültigen Bereich angegeben.
unauthenticated Die Anfrage wurde aufgrund eines fehlenden, ungültigen oder abgelaufenen OAuth-Tokens nicht authentifiziert.
permission-denied Der Client hat keine ausreichende Berechtigungen oder die UI wird in einer nicht autorisierten Domain gehostet.
not-found Die angegebene Ressource wurde nicht gefunden.
aborted Es ist ein Konflikt aufgrund von gleichzeitig ausgeführten Aktionen aufgetreten, beispielsweise ein Read-Modify-Write-Konflikt.
already-exists Die Ressource, die ein Client erstellen möchte, ist bereits vorhanden.
resource-exhausted Entweder ist das Ressourcenkontingent aufgebraucht oder die Ratenbegrenzung wurde erreicht.
cancelled Die Anfrage wurde vom Client abgebrochen.
data-loss Es ist ein dauerhafter Datenverlust oder Datenkorruption aufgetreten.
unknown Unbekannter Serverfehler.
internal Interner Serverfehler.
not-implemented Die API-Methode wurde vom Server nicht implementiert.
unavailable Dienst ist nicht verfügbar.
restart-process Sie müssen die URL, über die Sie auf diese Seite weitergeleitet wurden, noch einmal aufrufen und den Authentifizierungsprozess neu starten.
deadline-exceeded Die Frist der Anfrage wurde überschritten.
authentication-uri-fail Authentifizierungs-URI konnte nicht generiert werden.
gcip-token-invalid Ein ungültiges GCIP-ID-Token wurde angegeben.
gcip-redirect-invalid Ungültige Weiterleitungs-URL.
get-project-mapping-fail Fehler beim Abrufen der Projekt-ID.
gcip-id-token-encryption-error Fehler bei der GCIP-ID-Token-Verschlüsselung.
gcip-id-token-decryption-error Fehler bei der Entschlüsselung des GCIP-ID-Tokens.
gcip-id-token-unescape-error Fehler beim Aufheben der Escapezeichen für die websichere Base64-Codierung.
resource-missing-gcip-sign-in-url GCIP-Authentifizierungs-URL für die angegebene IAP-Ressource ist nicht vorhanden.

Benutzeroberfläche anpassen

Sie können Ihre Authentifizierungsseite mit optionalen Funktionen wie Fortschrittsanzeigen und Abmeldeseiten anpassen.

Fortschrittsanzeige darstellen

Wenn Sie dem Nutzer eine benutzerdefinierte Fortschritts-UI anzeigen möchten, wenn das gcip-iap-Modul zeitaufwendige Netzwerkaufgaben ausführt, implementieren Sie showProgressBar() und hideProgressBar().

Nutzer abmelden

In einigen Fällen kann es sinnvoll sein, Nutzern das Abmelden von allen aktuellen Sitzungen zu erlauben, die die gleiche Authentifizierungs-URL nutzen.

Nachdem sich ein Nutzer abgemeldet hat, gibt es möglicherweise keine URL, zu der er zurückgeleitet werden kann. Dies geschieht in der Regel, wenn sich ein Nutzer von allen Mandanten abmeldet, die mit einer Anmeldeseite verknüpft sind. Implementieren Sie in diesem Fall completeSignOut(), um eine Meldung anzuzeigen, die darauf hinweist, dass sich der Nutzer erfolgreich abgemeldet hat. Wenn Sie diese Methode nicht implementieren, wird eine leere Seite angezeigt, wenn sich ein Nutzer abmeldet.

Nutzer verarbeiten

Wenn Sie einen angemeldeten Nutzer ändern möchten, bevor Sie zur IAP-Ressource weiterleiten, implementieren Sie processUser().

Mit dieser Methode können Sie Folgendes tun:

  • Mit weiteren Anbietern verknüpfen
  • Nutzerprofil aktualisieren
  • Nutzer nach der Registrierung zur Eingabe zusätzlicher Daten auffordern
  • OAuth-Zugriffstoken verarbeiten, die von getRedirectResult() nach dem Aufruf von signInWithRedirect() zurückgegeben werden

Im Folgenden finden Sie ein Beispiel für die Implementierung von 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;
  });
}

Wenn Änderungen an einem Nutzer in den ID-Token-Ansprüchen berücksichtigt werden sollen, die von IAP an Ihre App weitergegeben werden, müssen Sie das Token aktualisieren:

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

Benutzeroberfläche testen

Nachdem Sie eine Klasse erstellt haben, die AuthenticationHandler implementiert, können Sie sie verwenden, um eine neue Authentication-Instanz zu erstellen und zu starten:

// Implement interface AuthenticationHandler.
// const authHandlerImplementation = ....
const ciapInstance = new ciap.Authentication(authHandlerImplementation);
ciapInstance.start();

Stellen Sie Ihre Anwendung bereit und rufen Sie die Authentifizierungsseite auf. Anschließend sollte Ihre benutzerdefinierte Anmeldungs-UI angezeigt werden.

Nächste Schritte