Anmeldeseite mit FirebaseUI erstellen

Damit externe Identitäten mit Identity-Aware Proxy (IAP) verwendet werden können, benötigt Ihre Anwendung eine Anmeldeseite. IAP leitet Nutzer auf diese Seite weiter, damit sie sich authentifizieren, bevor sie auf sichere Ressourcen zugreifen können.

In diesem Artikel wird gezeigt, wie Sie mithilfe von FirebaseUI, einer Open-Source-JavaScript-Bibliothek, eine Authentifizierungsseite erstellen. FirebaseUI bietet anpassbare Elemente, die helfen, den Boilerplate-Code zu reduzieren, und handhabt die Abläufe für die Anmeldung von Nutzern mit einer Vielzahl von Identitätsanbietern.

Zum schnellen Einstieg können Sie IAP die UI für Sie hosten lassen. Dadurch können Sie externe Identitäten ausprobieren, ohne zusätzlichen Code schreiben zu müssen. In komplexeren Szenarien können Sie auch Ihre eigene Anmeldeseite erstellen. Diese Option ist komplexer, bietet jedoch vollständige Kontrolle über den Authentifizierungsablauf und die Nutzererfahrung.

Hinweise

Aktivieren Sie externe Identitäten und wählen Sie bei der Einrichtung die Option I'll provide my own UI aus.

Bibliotheken installieren

Installieren Sie die gcip-iap-, firebase- und firebaseui-Bibliotheken. Das Modul gcip-iap abstrahiert die Kommunikation zwischen Ihrer Anwendung, IAP und Identity Platform. Die firebase- und firebaseui-Bibliotheken enthalten die Bausteine für Ihre Authentifizierungs-UI.

npm install firebase --save
npm install firebaseui --save
npm install gcip-iap --save

Beachten Sie, dass das Modul gcip-iap bei Verwendung von CDN nicht verfügbar ist.

Sie können die Module dann mit import in Ihre Quelldateien importieren. Verwenden Sie die korrekten Importe für Ihre SDK-Version:

gcip-iap v0.1.4 oder älter

// Import firebase modules.
import * as firebase from "firebase/app";
import "firebase/auth";
// Import firebaseui module.
import * as firebaseui from 'firebaseui'
// Import gcip-iap module.
import * as ciap from 'gcip-iap';

gcip-iap v1.0.0 oder höher

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

  • Aktualisieren Sie die Versionen firebase und firebaseui in der Datei package.json auf v9.6.0+ bzw. v6.0.0+.
  • Aktualisieren Sie die Importanweisungen firebase so:
// Import firebase modules.
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// Import firebaseui module.
import * as firebaseui from 'firebaseui'
// Import gcip-iap module.

Es sind keine weiteren Codeänderungen erforderlich.

Weitere Installationsoptionen, darunter die Verwendung lokalisierter Versionen der Bibliotheken, finden Sie in der Anleitung auf GitHub.

Anwendung konfigurieren

FirebaseUI verwendet ein Konfigurationsobjekt, das die Mandanten und Anbieter angibt, die für die Authentifizierung verwendet werden sollen. Eine komplette Konfiguration kann sehr umfangreich sein und in etwa so aussehen:

// The project configuration.
const configs = {
  // Configuration for project identified by API key API_KEY1.
  API_KEY1: {
    authDomain: 'project-id1.firebaseapp.com',
    // Decide whether to ask user for identifier to figure out
    // what tenant to select or whether to present all the tenants to select from.
    displayMode: 'optionFirst', // Or identifierFirst
    // The terms of service URL and privacy policy URL for the page
    // where the user select tenant or enter email for tenant/provider
    // matching.
    tosUrl: 'http://localhost/tos',
    privacyPolicyUrl: 'http://localhost/privacypolicy',
    callbacks: {
      // The callback to trigger when the selection tenant page
      // or enter email for tenant matching page is shown.
      selectTenantUiShown: () => {
        // Show title and additional display info.
      },
      // The callback to trigger when the sign-in page
      // is shown.
      signInUiShown: (tenantId) => {
        // Show tenant title and additional display info.
      },
      beforeSignInSuccess: (user) => {
        // Do additional processing on user before sign-in is
        // complete.
        return Promise.resolve(user);
      }
    },
    tenants: {
      // Tenant configuration for tenant ID tenantId1.
      tenantId1: {
        // Full label, display name, button color and icon URL of the
        // tenant selection button. Only needed if you are
        // using the option first option.
        fullLabel: 'ACME Portal',
        displayName: 'ACME',
        buttonColor: '#2F2F2F',
        iconUrl: '<icon-url-of-sign-in-button>',
         // Sign-in providers enabled for tenantId1.
        signInOptions: [
          // Microsoft sign-in.
          {
            provider: 'microsoft.com',
            providerName: 'Microsoft',
            buttonColor: '#2F2F2F',
            iconUrl: '<icon-url-of-sign-in-button>',
            loginHintKey: 'login_hint'
          },
          // Email/password sign-in.
          {
            provider: 'password',
            // Do not require display name on sign up.
            requireDisplayName: false,
            disableSignUp: {
              // Disable user from signing up with email providers.
              status: true,
              adminEmail: 'admin@example.com',
              helpLink: 'https://www.example.com/trouble_signing_in'
            }
          },
          // SAML provider. (multiple SAML providers can be passed)
          {
            provider: 'saml.my-provider1',
            providerName: 'SAML provider',
            fullLabel: 'Employee Login',
            buttonColor: '#4666FF',
            iconUrl: 'https://www.example.com/photos/my_idp/saml.png'
          },
        ],
        // If there is only one sign-in provider eligible for the user,
        // whether to show the provider selection page.
        immediateFederatedRedirect: true,
        signInFlow: 'redirect', // Or popup
        // The terms of service URL and privacy policy URL for the sign-in page
        // specific to each tenant.
        tosUrl: 'http://localhost/tenant1/tos',
        privacyPolicyUrl: 'http://localhost/tenant1/privacypolicy'
      },
      // Tenant configuration for tenant ID tenantId2.
      tenantId2: {
        fullLabel: 'OCP Portal',
        displayName: 'OCP',
        buttonColor: '#2F2F2F',
        iconUrl: '<icon-url-of-sign-in-button>',
        // Tenant2 supports a SAML, OIDC and Email/password sign-in.
        signInOptions: [
          // Email/password sign-in.
          {
            provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
            // Do not require display name on sign up.
            requireDisplayName: false
          },
          // SAML provider. (multiple SAML providers can be passed)
          {
            provider: 'saml.my-provider2',
            providerName: 'SAML provider',
            fullLabel: 'Contractor Portal',
            buttonColor: '#4666FF',
            iconUrl: 'https://www.example.com/photos/my_idp/saml.png'
          },
          // OIDC provider. (multiple OIDC providers can be passed)
          {
            provider: 'oidc.my-provider1',
            providerName: 'OIDC provider',
            buttonColor: '#4666FF',
            iconUrl: 'https://www.example.com/photos/my_idp/oidc.png'
          },
        ],
      },
      // Tenant configuration for tenant ID tenantId3.
      tenantId3: {
        fullLabel: 'Tenant3 Portal',
        displayName: 'Tenant3',
        buttonColor: '#007bff',
        iconUrl: '<icon-url-of-sign-in-button>',
        // Tenant3 supports a Google and Email/password sign-in.
        signInOptions: [
          // Email/password sign-in.
          {
            provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
            // Do not require display name on sign up.
            requireDisplayName: false
          },
          // Google provider.
          {
            provider: 'google.com',
            scopes: ['scope1', 'scope2', 'https://example.com/scope3'],
            loginHintKey: 'login_hint',
            customParameters: {
              prompt: 'consent',
            },
          },
        ],
        // Sets the adminRestrictedOperation configuration for providers
        // including federated, email/password, email link and phone number.
        adminRestrictedOperation: {
          status: true,
          adminEmail: 'admin@example.com',
          helpLink: 'https://www.example.com/trouble_signing_in'
        }
      },
    },
  },
};

In den folgenden Abschnitten wird gezeigt, wie Sie einige spezifische IAP-Felder konfigurieren. Beispiele zum Festlegen anderer Felder finden Sie im Code-Snippet oben oder in der FirebaseUI-Dokumentation auf GitHub.

API-Schlüssel einrichten

Eine typische Konfiguration beginnt mit einem API-Schlüssel für Ihr Projekt:

// The project configuration.
const configs = {
  // Configuration for API_KEY.
  API_KEY: {
    // Config goes here
  }
}

In den meisten Fällen müssen Sie nur einen API-Schlüssel angeben. Wenn Sie jedoch eine einzelne Authentifizierungs-URL für mehrere Projekte verwenden möchten, können Sie auch mehrere API-Schlüssel nutzen:

const configs = {
  API_KEY1: {
    // Config goes here
  },
  API_KEY2: {
    // Config goes here
  },
}

Authentifizierungsdomain abrufen

Geben Sie für das Feld authdomain die bereitgestellte Domain für eine föderierte Anmeldung an. Sie können dieses Feld in der Google Cloud Console auf der Seite „Identity Platform“ abrufen.

Mandanten-IDs festlegen

Für die Konfiguration ist eine Liste von Mandanten und Anbietern erforderlich, mit denen sich die Nutzer authentifizieren können.

Jeder Mandant wird anhand seiner ID identifiziert. Wenn Sie eine Authentifizierung auf Projektebene, also ohne Mandanten, nutzen, verwenden Sie stattdessen die spezielle Kennung _ als API-Schlüssel. Beispiel:

const configs = {
  // Configuration for project identified by API key API_KEY1.
  API_KEY1: {
    tenants: {
      // Project-level IdPs flow.
      _: {
        // Tenant config goes here
      },
      // Single tenant flow.
      1036546636501: {
        // Tenant config goes here
      }
    }
  }
}

Sie können mithilfe des Operators * auch eine Mandantenkonfiguration mit Platzhalter angeben. Dieser Mandant dient als Fallback, wenn keine entsprechende ID gefunden wird.

Mandantenanbieter konfigurieren

Jeder Mandant hat seine eigenen Anbieter. Diese werden im Feld signInOptions angegeben:

tenantId1: {
  signInOptions: [
    // Options go here
  ]
}

Informationen zum Konfigurieren von Anbietern erhalten Sie in der FirebaseUI-Dokumentation unter Log-in-Anbieter konfigurieren.

Zusätzlich zu den in der FirebaseUI-Dokumentation beschriebenen Schritten gibt es mehrere IAP-spezifische Felder, die vom festgelegten Mandantenauswahlmodus abhängig sind. Weitere Informationen zu diesen Feldern finden Sie im nächsten Abschnitt.

Mandantenauswahlmodus festlegen

Nutzer haben zwei Möglichkeiten für die Auswahl eines Mandanten: den Optionsmodus und den Kennungsmodus.

Im Optionsmodus wählt der Nutzer zuerst einen Mandanten aus einer Liste aus und gibt dann seinen Nutzernamen und sein Passwort ein. Im Kennungsmodus gibt der Nutzer zuerst seine E-Mail-Adresse ein. Das System wählt dann automatisch den ersten Mandanten mit einem Identitätsanbieter aus, bei dem eine Übereinstimmung mit der E-Mail-Domain besteht.

Zur Verwendung des Optionsmodus legen Sie für displayMode den Wert optionFirst fest. Anschließend müssen Sie Konfigurationsangaben für die einzelnen Schaltflächen des Mandanten bereitstellen, einschließlich displayName, buttonColor und iconUrl. Ein optionales fullLabel kann auch angegeben werden, um das gesamte Schaltflächenlabel statt nur den Anzeigenamen zu überschreiben.

Im Folgenden finden Sie das Beispiel für einen Mandanten, der für die Verwendung des Optionsmodus konfiguriert wird:

tenantId1: {
  fullLabel: 'ACME Portal',
  displayName: 'ACME',
  buttonColor: '#2F2F2F',
  iconUrl: '<icon-url-of-sign-in-button>',
  // ...

Zur Verwendung des Kennungsmodus muss für jede Anmeldeoption das Feld hd angegeben werden. Darin wird festgelegt, welche Domain unterstützt wird. Dies kann entweder ein regulärer Ausdruck (z. B. /@example\.com$/) oder der Domainstring (z. B. example.com) sein.

Der folgende Code ist ein Beispiel für einen Mandanten, der für die Verwendung des Kennungsmodus konfiguriert wird:

tenantId1: {
  signInOptions: [
    // Email/password sign-in.
    {
      hd: 'acme.com', // using regex: /@acme\.com$/
      // ...
    },

Direkte Weiterleitung aktivieren

Unterstützt Ihre Anwendung nur einen Identitätsanbieter, kann die Anmelde-UI durch Festlegen von true für immediateFederatedRedirect übersprungen und der Nutzer direkt an den Anbieter weitergeleitet werden.

Callbacks einrichten

Das Konfigurationsobjekt enthält eine Reihe von Callbacks, die an verschiedenen Punkten der Authentifizierung aufgerufen werden. Damit können Sie die UI weiter anpassen. Folgende Hooks sind verfügbar:

selectTenantUiShown() Wird ausgelöst, wenn die UI zum Auswählen eines Mandanten angezeigt wird. Verwenden Sie diese Option, wenn Sie die UI ändern und einen benutzerdefinierten Titel oder ein benutzerdefiniertes Design nutzen möchten.
signInUiShown(tenantId) Wird ausgelöst, wenn ein Mandant ausgewählt und für den Nutzer die UI angezeigt wird, in der er seine Anmeldedaten eingeben soll. Verwenden Sie diese Option, wenn Sie die UI ändern und einen benutzerdefinierten Titel oder ein benutzerdefiniertes Design nutzen möchten.
beforeSignInSuccess(user) Wird vor dem Abschluss der Anmeldung ausgelöst. Verwenden Sie diese Option, um Änderungen für einen angemeldeten Nutzer vorzunehmen, bevor die Weiterleitung zur IAP-Ressource erfolgt.

Im folgenden Beispielcode sehen Sie, wie Sie diese Callbacks implementieren können:

callbacks: {
  selectTenantUiShown: () => {
    // Show info of the IAP resource.
    showUiTitle(
        'Select your employer to access your Health Benefits');
  },
  signInUiShown: (tenantId) => {
    // Show tenant title and additional display info.
    const tenantName = getTenantNameFromId(tenantId);
    showUiTitle(`Sign in to access your ${tenantName} Health Benefits`);
  },
  beforeSignInSuccess: (user) => {
    // Do additional processing on user before sign-in is
    // complete.
    // For example update the user profile.
    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;
    });
  }
}

Bibliothek initialisieren

Wenn Sie ein Konfigurationsobjekt erstellt haben, folgen Sie diesen Schritten, um die Bibliothek auf Ihrer Authentifizierungsseite zu initialisieren:

  1. Erstellen Sie einen HTML-Container, in dem die UI gerendert werden soll.

    <!DOCTYPE html>
    <html>
     <head>...</head>
     <body>
       <!-- The surrounding HTML is left untouched by FirebaseUI.
            Your app may use that space for branding, controls and other
            customizations.-->
       <h1>Welcome to My Awesome App</h1>
       <div id="firebaseui-auth-container"></div>
     </body>
    </html>
    
  2. Erstellen Sie eine FirebaseUiHandler-Instanz zum Rendern im HTML-Container und übergeben Sie das von Ihnen erstellte config-Element an diese Instanz.

    const configs = {
      // ...
    }
    const handler = new firebaseui.auth.FirebaseUiHandler(
      '#firebaseui-auth-container', configs);
    
  3. Erstellen Sie eine neue Authentication-Instanz, übergeben Sie den Handler an diese Instanz und rufen Sie start() auf.

    const ciapInstance = new ciap.Authentication(handler);
    ciapInstance.start();
    

Stellen Sie Ihre Anwendung bereit und rufen Sie die Authentifizierungsseite auf. Es sollte dann eine Anmelde-UI mit Ihren Mandanten und Anbietern angezeigt werden.

Nächste Schritte