Nutzer in App Engine anmelden

In dieser Anleitung wird beschrieben, wie Sie Anmeldedaten von Drittanbietern mithilfe von Identity Platform, der App Engine-Standardumgebung und Datastore abrufen, prüfen und speichern.

Dazu werden Sie Schritt für Schritt durch eine einfache Anwendung für Notizen namens Firenotes geführt, die die Notizen von Nutzern in deren eigenen privaten Notizbüchern speichert. Die Notizbücher werden pro Nutzer gespeichert und durch die eindeutige Identity Platform-ID des jeweiligen Nutzers identifiziert. Die Anwendung beinhaltet diese Komponenten:

  • Das Front-End konfiguriert die Benutzeroberfläche für die Anmeldung und ruft die Identity Platform-ID ab. Außerdem verarbeitet es Änderungen am Authentifizierungsstatus und ermöglicht Nutzern, ihre Notizen anzusehen.

  • FirebaseUI ist eine Open-Source-Drop-in-Lösung, die Authentifizierungs- und UI-Aufgaben vereinfacht. Das SDK verarbeitet unter anderem die Nutzeranmeldung, das Verknüpfen von mehreren Anbietern mit einem einzigen Konto und das Wiederherstellen von Passwörtern. Die Lösung implementiert Best Practices für die Authentifizierung, um für eine reibungslose und sichere Anmeldung zu sorgen.

  • Das Back-End überprüft den Authentifizierungsstatus des Nutzers und gibt Nutzerprofilinformationen sowie die Notizen des Nutzers zurück.

Die Anwendung speichert die Nutzeranmeldedaten mithilfe der NDB-Clientbibliothek in Datastore. Sie können die Anmeldedaten aber auch in einer Datenbank Ihrer Wahl speichern.

Firenotes basiert auf dem Framework für Flask-Webanwendungen. Die Beispielanwendung verwendet Flask der Einfachheit und Benutzerfreundlichkeit wegen, die untersuchten Konzepte und Technologien gelten aber unabhängig von dem verwendeten Framework.

Ziele

In dieser Anleitung lernen Sie Folgendes:

  • Benutzeroberfläche mit FirebaseUI für Identity Platform konfigurieren
  • Identity Platform-ID-Token abrufen und mit der serverseitigen Authentifizierung prüfen
  • Anmeldedaten und verknüpfte Daten in Cloud Datastore speichern
  • Datenbank mit der NDB-Clientbibliothek abfragen
  • Anwendung in App Engine bereitstellen

Kosten

In dieser Anleitung werden kostenpflichtige Komponenten von Google Cloud verwendet, darunter:

  • Datastore
  • Identity Platform

Sie können mithilfe des Preisrechners eine Kostenschätzung für Ihre voraussichtliche Nutzung erstellen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Vorbereitung

  1. Installieren Sie Git, Python 2.7 und virtualenv. Weitere Informationen zum Einrichten Ihrer Python-Entwicklungsumgebung für Google Cloud, beispielsweise zum Installieren der neuesten Version von Python, finden Sie unter Python-Entwicklungsumgebung einrichten.
  2. Melden Sie sich bei Ihrem Google-Konto an.

    Wenn Sie noch kein Konto haben, melden Sie sich hier für ein neues Konto an.

  3. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  4. Installieren und initialisieren Sie das Cloud SDK.

Wenn Sie das SDK bereits in einem anderen Projekt installiert und initialisiert haben, legen Sie das gcloud-Projekt auf die App Engine-Projekt-ID fest, die Sie für Firenotes verwenden. Weitere Informationen zu bestimmten Befehlen zum Aktualisieren eines Projekts mit dem gcloud-Tool finden Sie unter Cloud SDK-Konfigurationen verwalten.

Beispielanwendung klonen

So laden Sie das Beispiel auf Ihren lokalen Computer herunter:

  1. Klonen Sie das Beispielanwendungs-Repository auf Ihren lokalen Rechner:

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Sie können auch das Beispiel als ZIP-Datei herunterladen und extrahieren.

  2. Wechseln Sie in das Verzeichnis mit dem Beispielcode:

    cd python-docs-samples/appengine/standard/firebase/firenotes
    

Benutzeroberfläche hinzufügen

So konfigurieren Sie FirebaseUI für Identity Platform und aktivieren Identitätsanbieter:

  1. Führen Sie folgende Schritte aus, um Identity Platform Ihrer Anwendung hinzuzufügen:

    1. Rufen Sie die Cloud Console auf.
      Zur Cloud Console
    2. Wählen Sie das Google Cloud-Projekt aus, das Sie verwenden möchten:
      • Wenn Sie bereits ein Projekt haben, wählen Sie es aus der Drop-down-Liste Organisation auswählen oben auf der Seite aus.
      • Wenn Sie noch kein Google Cloud-Projekt haben, erstellen Sie ein neues Projekt in der Cloud Console.
    3. Rufen Sie in der Cloud Console die Seite Identity Platform auf.
      Zur Seite "Identity Platform"
    4. Klicken Sie auf der Seite "Identity Platform" auf Customer Identity aktivieren.
    5. Rufen Sie in der Cloud Console die Customer Identity-Seite Nutzer auf.
      Zur Seite "Nutzer"
    6. Klicken Sie rechts oben auf Einrichtungsdetails für die Anwendung.
    7. Kopieren Sie die Details zur Einrichtung der Anwendung in Ihre Webanwendung.

      // Obtain the following from the "Add Firebase to your web app" dialogue
      // Initialize Firebase
      var config = {
        apiKey: "<API_KEY>",
        authDomain: "<PROJECT_ID>.firebaseapp.com",
        databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
        projectId: "<PROJECT_ID>",
        storageBucket: "<BUCKET>.appspot.com",
        messagingSenderId: "<MESSAGING_SENDER_ID>"
      };
  2. Bearbeiten Sie die Datei backend/app.yaml und geben Sie Ihre Google Cloud-Projekt-ID in die Umgebungsvariablen ein:

    runtime: python27
    api_version: 1
    threadsafe: true
    service: backend
    
    handlers:
    - url: /.*
      script: main.app
    
    env_variables:
      GAE_USE_SOCKETS_HTTPLIB : 'true'
    
  3. Konfigurieren Sie in der Datei frontend/main.js das FirebaseUI-Anmelde-Widget. Wählen Sie dazu die Anbieter aus, die Sie Ihren Nutzern zur Verfügung stellen möchten.

      // Firebase log-in widget
      function configureFirebaseLoginWidget() {
        var uiConfig = {
          'signInSuccessUrl': '/',
          'signInOptions': [
            // Leave the lines as is for the providers you want to offer your users.
            firebase.auth.GoogleAuthProvider.PROVIDER_ID,
            firebase.auth.FacebookAuthProvider.PROVIDER_ID,
            firebase.auth.TwitterAuthProvider.PROVIDER_ID,
            firebase.auth.GithubAuthProvider.PROVIDER_ID,
            firebase.auth.EmailAuthProvider.PROVIDER_ID
          ],
          // Terms of service url
          'tosUrl': '<your-tos-url>',
        };
    
        var ui = new firebaseui.auth.AuthUI(firebase.auth());
        ui.start('#firebaseui-auth-container', uiConfig);
      }
    
      // Fetch notes from the backend.
      function fetchNotes() {
        $.ajax(backendHostUrl + '/notes', {
          /* Set header for the XMLHttpRequest to get data from the web server
          associated with userIdToken */
          headers: {
            'Authorization': 'Bearer ' + userIdToken
          }
        }).then(function(data){
          $('#notes-container').empty();
          // Iterate over user data to display user's notes from database.
          data.forEach(function(note){
            $('#notes-container').append($('<p>').text(note.message));
          });
        });
      }
    
      // Sign out a user
      var signOutBtn =$('#sign-out');
      signOutBtn.click(function(event) {
        event.preventDefault();
    
        firebase.auth().signOut().then(function() {
          console.log("Sign out successful");
        }, function(error) {
          console.log(error);
        });
      });
    
      // Save a note to the backend
      var saveNoteBtn = $('#add-note');
      saveNoteBtn.click(function(event) {
        event.preventDefault();
    
        var noteField = $('#note-content');
        var note = noteField.val();
        noteField.val("");
    
        /* Send note data to backend, storing in database with existing data
        associated with userIdToken */
        $.ajax(backendHostUrl + '/notes', {
          headers: {
            'Authorization': 'Bearer ' + userIdToken
          },
          method: 'POST',
          data: JSON.stringify({'message': note}),
          contentType : 'application/json'
        }).then(function(){
          // Refresh notebook display.
          fetchNotes();
        });
    
      });
    
      configureFirebaseLogin();
      configureFirebaseLoginWidget();
    
    });
    
  4. Aktivieren Sie in der Cloud Console die Anbieter, die Sie beibehalten haben:

    1. Rufen Sie in der Cloud Console die Customer Identity-Seite Anbieter auf.
      Zur Seite "Anbieter"
    2. Klicken Sie auf Anbieter hinzufügen.
    3. Wählen Sie in der Drop-down-Liste Anbieter auswählen die Anbieter aus, die Sie verwenden möchten.
    4. Klicken Sie neben Aktiviert auf die Schaltfläche, um den Anbieter zu aktivieren.
      • Wenn Sie einen externen Identitätsanbieter verwenden, geben Sie die Anbieter-ID und das Secret von der Entwicklerwebsite des Anbieters ein. Die Firebase-Dokumentation enthält spezifische Anleitungen in den Abschnitten "Vorbereitung" der Leitfäden für Facebook, Twitter und GitHub.
      • Informationen zum Einbinden von SAML und OIDC finden Sie in der Konfiguration Ihres IdP.
  5. Nehmen Sie Ihre Domain in die Liste der autorisierten Domains in Identity Platform auf:

    1. Rufen Sie in der Cloud Console die Customer Identity-Seite Einstellungen auf.
      Zur Seite "Einstellungen"
    2. Klicken Sie unter Autorisierte Domains auf Domain hinzufügen.
    3. Geben Sie die Domain Ihrer Anwendung im folgenden Format ein:

      [PROJECT_ID].appspot.com
      

      Fügen Sie nicht http:// vor dem Domainnamen ein.

Abhängigkeiten installieren

  1. Wechseln Sie in das Verzeichnis backend und schließen Sie die Anwendungseinrichtung ab:

    cd backend/
    
  2. Installieren Sie die Abhängigkeiten in einem lib-Verzeichnis im Projekt:

    pip install -t lib -r requirements.txt
    
  3. In appengine_config.py registriert die Methode vendor.add() die Bibliotheken im Verzeichnis lib.

Anwendung lokal ausführen

Wenn Sie die Anwendung lokal ausführen möchten, verwenden Sie den lokalen App Engine-Entwicklungsserver:

  1. Fügen Sie der Datei die folgende URL als backendHostURL in main.js hinzu:

    http://localhost:8081

  2. Rufen Sie das Stammverzeichnis der Anwendung auf und starten Sie dann den Entwicklungsserver:

    dev_appserver.py frontend/app.yaml backend/app.yaml
    
  3. Rufen Sie in einem Webbrowser http://localhost:8080/ auf.

Nutzer auf dem Server authentifizieren

Nachdem Sie ein Projekt eingerichtet und eine Anwendung für die Entwicklung initialisiert haben, können Sie den Code durchgehen, um zu erfahren, wie Identity Platform-ID-Tokens auf dem Server abgerufen und geprüft werden.

ID-Token von Identity Platform abrufen

Der erste Schritt der serverseitigen Authentifizierung besteht im Abrufen eines Zugriffstokens für die Überprüfung. Authentifizierungsanfragen werden mit dem Listener onAuthStateChanged() von Identity Platform verarbeitet:

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    $('#logged-out').hide();
    var name = user.displayName;

    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    var welcomeName = name ? name : user.email;

    user.getIdToken().then(function(idToken) {
      userIdToken = idToken;

      /* Now that the user is authenicated, fetch the notes. */
      fetchNotes();

      $('#user').text(welcomeName);
      $('#logged-in').show();

    });

  } else {
    $('#logged-in').hide();
    $('#logged-out').show();

  }
});

Wenn ein Nutzer angemeldet ist, gibt die Identity Platform-Methode getToken() im Callback ein Identity Platform-ID-Token in Form eines JSON Web Tokens (JWT) zurück.

Tokens auf dem Server überprüfen

Nachdem sich ein Nutzer angemeldet hat, ruft der Front-End-Dienst alle vorhandenen Notizen im Notizbuch des Nutzers durch eine AJAX-GET-Anfrage ab. Dafür ist eine Autorisierung für den Zugriff auf die Daten des Nutzers erforderlich, sodass das JWT im Authorization-Header der Anfrage mithilfe des Schemas Bearer gesendet wird:

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  })

Bevor der Client auf Serverdaten zugreifen kann, muss von Ihrem Server geprüft werden, ob das Token von Identity Platform signiert ist. Sie können das Token mit der Google-Authentifizierungsbibliothek für Python überprüfen. Verwenden Sie die Funktion verify_firebase_token der Authentifizierungsbibliothek, um das Inhabertoken zu prüfen und die Anforderungen zu extrahieren:

id_token = request.headers['Authorization'].split(' ').pop()
claims = google.oauth2.id_token.verify_firebase_token(
    id_token, HTTP_REQUEST, audience=os.environ.get('GOOGLE_CLOUD_PROJECT'))
if not claims:
    return 'Unauthorized', 401

Jeder Identitätsanbieter sendet einen anderen Satz von Anforderungen. Jeder hat aber zumindest eine sub-Anforderung mit einer eindeutigen Nutzer-ID und eine Anforderung, über die einige Profilinformationen bereitgestellt werden, beispielsweise name oder email. Damit können Sie Ihre Anwendung für die einzelnen Nutzer personalisieren.

Nutzerdaten in Datastore verwalten

Nach der Authentifizierung eines Nutzers müssen Sie seine Daten speichern, damit sie nach dem Ende einer angemeldeten Sitzung erhalten bleiben. In den folgenden Abschnitten wird erläutert, wie Sie eine Notiz als Datastore-Entität speichern und Entitäten nach Nutzer-ID trennen.

Entitäten zum Speichern von Nutzerdaten erstellen

Sie können in Datastore eine Entität erstellen. Dazu deklarieren Sie eine NDB-Modellklasse mit bestimmten Attributen wie Ganzzahlen oder Strings. Datastore indexiert Entitäten nach der Art. In Firenotes ist die Art jeder Entität Note. Zum Zweck der Abfrage wird jede Note mit einem Schlüsselnamen gespeichert. Dies ist die Nutzer-ID, die im vorherigen Abschnitt aus der sub-Anforderung abgerufen wurde.

Der folgende Code zeigt, wie Attribute einer Entität festgelegt werden – sowohl mit der Konstruktormethode für die Modellklasse, wenn die Entität erstellt wird, als auch durch die Zuweisung einzelner Attribute nach der Erstellung:

data = request.get_json()

# Populates note properties according to the model,
# with the user ID as the key name.
note = Note(
    parent=ndb.Key(Note, claims['sub']),
    message=data['message'])

# Some providers do not provide one of these so either can be used.
note.friendly_id = claims.get('name', claims.get('email', 'Unknown'))

Damit Sie die neu erstellte Note in Datastore schreiben können, rufen Sie die Methode put() für das Objekt note auf.

Nutzerdaten abrufen

Wenn Sie Nutzerdaten abrufen möchten, die mit einer bestimmten Nutzer-ID verknüpft sind, verwenden Sie die NDB-Methode query(), um in der Datenbank nach Notizen in derselben Entitätengruppe zu suchen. Entitäten in derselben Gruppe oder im selben Ancestor-Pfad verwenden einen gemeinsamen Schlüsselnamen. In diesem Fall ist dies die Nutzer-ID.

def query_database(user_id):
    """Fetches all notes associated with user_id.

    Notes are ordered them by date created, with most recent note added
    first.
    """
    ancestor_key = ndb.Key(Note, user_id)
    query = Note.query(ancestor=ancestor_key).order(-Note.created)
    notes = query.fetch()

    note_messages = []

    for note in notes:
        note_messages.append({
            'friendly_id': note.friendly_id,
            'message': note.message,
            'created': note.created
        })

    return note_messages

Anschließend können Sie die Abfragedaten abrufen und sich die Notizen im Client ansehen:

// Fetch notes from the backend.
function fetchNotes() {
  $.ajax(backendHostUrl + '/notes', {
    /* Set header for the XMLHttpRequest to get data from the web server
    associated with userIdToken */
    headers: {
      'Authorization': 'Bearer ' + userIdToken
    }
  }).then(function(data){
    $('#notes-container').empty();
    // Iterate over user data to display user's notes from database.
    data.forEach(function(note){
      $('#notes-container').append($('<p>').text(note.message));
    });
  });
}

Anwendung bereitstellen

Sie haben Identity Platform erfolgreich in Ihre App Engine-Anwendung eingebunden. So sehen Sie, wie Ihre Anwendung in einer Live-Produktionsumgebung ausgeführt wird:

  1. Ändern Sie die Back-End-Host-URL in main.js zu https://backend-dot-[PROJECT_ID].appspot.com. Ersetzen Sie [PROJECT_ID] durch Ihre Projekt-ID.
  2. Stellen Sie die Anwendung mit der Cloud SDK-Befehlszeile bereit:

    gcloud app deploy backend/index.yaml frontend/app.yaml backend/app.yaml
    
  3. Sehen Sie sich die Anwendung live unter https://[PROJECT_ID].appspot.com an.

Bereinigen

Löschen Sie Ihr App Engine-Projekt, damit Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen nicht in Rechnung gestellt werden.

Projekt löschen

Am einfachsten vermeiden Sie weitere Kosten, wenn Sie das zum Ausführen der Anleitung erstellte Projekt löschen.

So löschen Sie das Projekt:

  1. Wechseln Sie in der Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Weitere Informationen